why is it too complex for the compiler to determine the closure result here?
_ = { // error: Unable to infer complex closure return type; add explicit type to disambiguate
_ = "" // or anything else here
return ""
}
why is it too complex for the compiler to determine the closure result here?
_ = { // error: Unable to infer complex closure return type; add explicit type to disambiguate
_ = "" // or anything else here
return ""
}
Swift only infers a closure's type if it is a single expression (without more context like Array.filter
)
Filed [SR-14824] Improve diagnostic for multi-statement closures instead of saying "too complex closure return type" · Issue #57172 · apple/swift · GitHub to improve the diagnostic.
i see. in my code it's something like:
{
bar($0)
}
which suddenly getting transformed into a much messier version should i merely add a print statement:
{ p -> SomeGeneric<Type> in
print(p)
return bar(p)
}
and obviously compiler has all the knowledge about return type: if i get the explicit type annotation wrong - compiler will complain, so instead of requiring it and complaining about a type mismatch it could have silently figured out what's needed. i guess i just have to live with this as this is a feature, not a bug.
lifehack version:
{
(
print($0),
return: bar($0)
).return
}
or a more cryptic:
{
(
print($0),
bar($0)
).1
}
The version with the label is a lot safer, as you don't need to remember to change the index if you add more expressions to the tuple. (Although I'm not a fan of either of these.)
Another alternative is to make the call site use infer the type. Array.filter
is a good example of this approach:
extension Array {
public func filter(
_ predicate (Element) throws -> Bool
) -> [Element] {
// implementation doesn't matter
}
Then at the call site, we can have closures with more statements
let xs = [1,2,3,4,5,6,7,8]
.filter { number in
logAnaltyics(for: number)
logPerformance(for: number)
print(primeFactors(number))
return number.isMultiple(of: 3)
}
// xs == [3, 6]
We don't need to add any inference here.
would be great, although i can't figure out how. that's the over-simplified version of the actual code where i encountered the issue:
protocol Proto {}
func foo() -> Generic<Proto> {
fatalError()
}
struct Generic<T> {
var t: T
func bar<R>(execute: (T) -> Generic<R>) {
}
}
func baz() {
Generic(t: true).bar { p in // p -> Generic<Proto> in
print(p)
return foo()
}
}
The tough part with Generic<T>.bar<R>
is that you introduce the generic parameter R
in the return value of a callback (execute
). However, it is not obvious where this returned value is used. More specifically, where do you use foo()
in baz
? Unless your closure is escaping, return foo()
looks like it will never be used without more knowledge of Generic
uses.
yep, that's a stripped down snippet obviously. "execute" is used within "bar", and the closure is indeed escaping in the actual code.
i agree on both counts. would be quite awkward to actually use this workaround in real code.