tera
1
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 ""
}
atfelix
(Adam Felix)
2
Swift only infers a closure's type if it is a single expression (without more context like Array.filter)
3 Likes
tera
4
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.
tera
5
lifehack version:
{
(
print($0),
return: bar($0)
).return
}
or a more cryptic:
{
(
print($0),
bar($0)
).1
}
1 Like
tim1724
(Tim Buchheim)
6
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.)
atfelix
(Adam Felix)
7
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.
tera
8
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()
}
}
atfelix
(Adam Felix)
9
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.
tera
10
yep, that's a stripped down snippet obviously. "execute" is used within "bar", and the closure is indeed escaping in the actual code.
tera
11
i agree on both counts. would be quite awkward to actually use this workaround in real code.