Consider the following function:
func whatever(foo: @autoclosure () -> Int) {
let x = foo()
print(x)
}
Naturally, we can invoke it like this:
whatever(foo: 5)
// prints: 5
However providing an explicit closure argument causes the compiler to complain:
whatever(foo: { 5 })
// Error: Function produces expected type 'Int'; did you mean to call it with '()'?
Is this the intended? Reading the documentation for @autoclosure I did not find a statement about whether arguments are always wrapped, even when providing a closure. My understanding of @autoclosure was:
Take a closure argument. If the argument is not a closure but has the same type as the closure would return, wrap it.
However, the behaviour I'm seeing is: Wrap the argument no matter what.
A more elaborate example makes this seem very odd to me:
struct Defaults {
static var dispatcher: Defaults = ...
subscript<T>(setting: Setting<T>) -> T { ... }
struct Setting<T> {
let key: String
let defaultValue: () -> T
init(key: String, defaultValue: @escaping @autoclosure () -> T) {
self.key = key
self.defaultValue = defaultValue
}
}
}
extension Defaults.Setting {
static var nickname: Defaults.Setting<String> {
return Defaults.Setting(key: "__nickname", defaultValue: "Angela Merkel")
}
}
// Usage:
Defaults.dispatcher[.nickname] = "Emmanuel Macron"
Now let's say I want to hash the key of a Setting value:
extension Defaults.Setting {
var withHashedKey: Defaults.Setting<T> {
return Defaults.Setting(key: key.md5(), defaultValue: defaultValue)
// Error: Cannot convert return expression of type 'Defaults.Setting<() -> T>' to return type 'Defaults.Setting<T>'
}
}
To clarify: defaultValue is of type () -> T. Providing it to init(key: String, defaultValue: () -> T), in my expectation should just work, because the argument and parameter have the same type (while parameter is @autoclosure).
However, Swift seems to wrap the provided closure, effectively creating () -> () -> T, which creates Setting<() -> T> instead of Setting<T>.
I can work around this issue by declaring an init which takes an explicitly non-@autoclosure parameter:
extension Defaults.Setting {
init(key: String, defaultValue: @escaping () -> T) {
self.init(key: key, defaultValue: defaultValue)
}
}
What's really daunting is that I can simply forward to the init taking the @autoclosure parameter and it works.
Am I missing something here or is it just not possible by design in Swift to provide closure arguments to @autoclosure parameters?