Closed gohanlon closed 1 year ago
I'm working on adding this feature, and have come across a situation that gave me pause. Consider this macro definition:
@attached(peer)
public macro Init(
_ accessLevel: AccessLevelConfig? = nil,
default: Any? = nil,
escaping: Bool? = nil,
label: String? = nil
) = …
This macro definition actually works quite well. Xcode even checks the validity of the supplied default
expression while editing and supports autocomplete:
@MemberwiseInit
public struct SimpleDefault {
@Init(default: "Blob") let name: String
/*
internal init(
name: String = "Blob"
) {
self.name = name
}
*/
}
print(SimpleDefault())
// → SimpleDefault(name: "Blob")
let global = 42
@MemberwiseInit
public struct GlobalDefault<T: Numeric> {
@Init(default: global) let number: T
/*
internal init(
number: T = global
) {
self.number = number
}
*/
}
print(GlobalDefault())
// → GlobalDefault<Int>(number: 42)
@MemberwiseInit
public struct ClosureDefault {
@Init(default: { "Blob" }) let name: () -> String
/*
internal init(
name: @escaping () -> String = {
"Blob"
}
) {
self.name = name
}
*/
}
print(ClosureDefault())
// → ClosureDefault(name: (Function))
print(ClosureDefault().name())
// → Blob
@MemberwiseInit
public struct NilDefault {
@Init(default: nil) let name: String?
}
print(NilDefault())
// → NilDefault(name: nil)
At first glance, this seems natural, and perhaps that's all that matters here. But, the behavior is surprising, if you look too closely:
@Init()
: Because there's no argument given, default
semantically defaults to nil
, and means "no default value".@Init(default: nil)
: Yet when nil
is provided explicitly, it means default to nil
.This distinction is only possible due to the "meta-ness" of macros operating on syntax.
Indeed weird, but double optionals would be weirder IMO. I think in general it's okay for @Init()
and @Init(default: nil)
to be semantically different even if the underlying parameters converge to the same value. Somewhat reminds me of the intuitiveness dilemma in https://github.com/gohanlon/swift-memberwise-init-macro/issues/7.
Description
For
var
properties, both Swift's and MemberwiseInit's memberwise initializers allow for default values to be specified inline:resulting in the following
init
:However,
let
properties cannot be reassigned and therefore defaults can't be specified inline. Swift would need additional syntax.MemberwiseInit, with the addition of
@Init(default:)
:can generate the
init
, defaulting thelet
property:Diagnostics:
@Init(default:)
can't be applied to alet
property that's already been initialized inline.