Swift optional promotion vs generic overload resolution - swift

Please consider the following code:
protocol P {}
class X {}
class Y: P {}
func foo<T>(_ closure: (T) -> Void) { print(type(of: closure)) }
func foo<T>(_ closure: (T) -> Void) where T: P { print(type(of: closure)) }
let xClosure: (X?) -> Void = { _ in }
foo(xClosure) // prints "(Optional<X>) -> ()"
let yClosure: (Y?) -> Void = { _ in }
foo(yClosure) // prints "(Y) -> ()"
Why does the foo(yClosure) call resolve to the version of foo constrained to T: P?
I understand why that version prints what it prints,
what I don't see is why it gets called instead of the other one.
To me it seems that the non-P version would be a better match for T == (Y?) -> Void.
Sure, the constrained version is more specific, but it requires conversion
(an implicit conversion from (Y?) -> Void to (Y) -> Void),
while the non-P version could be called with no conversion.
Is there a way to fix this code in a way such that the P-constrained version gets called
only if the parameter type of the passed-in closure directly conforms to P,
without any implicit conversions?

Specificity seems to always trump variance conversions, according to my experiments. For example:
func bar<T>(_ x: [Int], _ y: T) { print("A") }
func bar<T: P>(_ x: [Any], _ y: T) { print("B") }
bar([1], Y()) // A
bar is more specific, but requires a variance conversion from [Int] to [Any].
For why you can convert from (Y?) -> Void to (P) -> Void, see this. Note that Y is a subtype of Y?, by compiler magic.
Since it is so consistent, this behaviour seems to be by design. Since you can't really make Y not a subtype of Y?, you don't have a lot of choices if you want to get the desired behaviour.
I have this work around, and I admit it's really ugly - make your own Optional type. Let's call it Maybe<T>:
enum Maybe<T> {
case some(T)
case none
// implement all the Optional methods if you want
}
Now, your (Maybe<Y>) -> Void won't be converted to (P) -> Void. Normally I wouldn't recommend this, but since you said:
in the real-world code where I encountered this, the closure has multiple params, any of them can be optional, so this would lead to a combinatorial explosion.
I thought reinventing Optional might be worth it.

Related

Generic parameter 'T' could not be inferred after assignment

// Xcode 11.6 / Swift 5
import Foundation
func f<T>(_: (T) -> Void) { }
#discardableResult
func g(_: Int) -> Int { 0 }
f { g($0) } // compiles fine
f { let _ = g($0) } // Generic parameter 'T' could not be inferred
In the above code, the generic function f expects as its argument a function that takes an argument of type T.
The function g takes an argument of type Int.
When I write f { g($0) }, the code compiles. I believe (please correct me if I'm wrong) this compiles because the compiler can infer that T is an Int based on g's argument type.
However, when I try to do something with the return value of g, for example in the let _ = g($0) line, the compiler complains that it can no longer infer the type of T.
It seems to me the return type of g should have no bearing on how the compiler infers T's type, but clearly it does.
Can anyone shed some light on why this happens, and what (if anything) can be done to correct it?
This may or may not be a compiler bug.
It is known that Swift does not try to infer the types of some closures, namely, multi-statement ones, as said in SR-1570:
This is correct behavior: Swift does not infer parameter or return types from the bodies of multi-statement closures.
However, your closure consists of only one statement, one declaration to be specific. It is possible, albeit weird, that they designed it so that Swift doesn't try to infer types if the closure contains one declaration as well. For example, this does not compile either
f { let x: Int = $0 } // nothing to do with "g"! The issue seems to be with declarations
If this were by-design, the rationale behind it might be because a single declaration in a closure doesn't make much sense anyway. Whatever is declared, won't be used.
But again, this is just speculation, and this could be a bug as well.
To fix it, simply make it a not-a-declaration:
f { _ = g($0) } // this, without the "let", is IMO the idiomatic way to ignore the function result
Or
f { g($0) } // g has #discardableResult anyway, so you don't even need the wildcard
The function f takes in a function as a parameter which in turn takes a parameter of type T and returns nothing (Void). For a closure to infer types automatically, it has to consist of single (and sometimes simple) expression. Anything complex makes it difficult for the compiler to infer (which makes sense from the compiler's standpoint). Apparently, let _ = g($0) is a complex statement as far as the compiler is concerned. For further information, see this mailing list discussion
it looks like #discardableResult gives you an ability to have 2 types of functions:
g(_: Int) -> Int and g(_: Int) -> Void (it is when you don't want to use a result of function)
I think that
f { g($0) } - here your f can infer a type because it has the same Type
(_: (T) -> Void) and (_: (Int) -> Void)
f { let _ = g($0) } - in this case the type of g function is different from f function
(_: (T) -> Void) and (_: (Int) -> Int)
If you will remove "let" it will compile again:
f { _ = g($0) }
I think that
f { let _ = g($0) } - return only Int value
f { _ = g($0) } - return function (_: (Int) -> Int)
Maybe it is a key here

Using Swift function that a function that takes a generic sequence

I'm picking swift up now and the generics are pretty different than what I'm used to. What is the right way to do something like this?
func createThing<T, Seq: Sequence>(_ type: T.Type, _ block : #escaping (_ sequence: Seq) -> Void) where Seq.Element == T {
// ...
}
enum MyEnum {
case A
case B
}
// error: generic parameter 'Seq' could not be inferred
createThing(MyEnum.Type, { sequence in
for i in sequence{
//...
}
})
I would love to just supply a generic type parameter directly with createThing<MyEnum>(...) but that apparently isn't something Swift can do and generics seem to work pretty different for protocols than they do everything else.
Seq is part of the generic signature of the createThing function, which means that the compiler either needs to be able to infer this from the calling context, or be explicitly be told what concrete implementation of Sequence should expect. Also placing the Sequence generic at the function level really limits what you can do within that function, since it cannot instantiate a protocol.
You can convert the (Seq) -> Void block to a (T) -> Void one, and move the sequence iteration in doSomething, this will remove the compile error. And while you're at it, you can add a default value for the type parameter, this will enable type inferring and automatic filling of that parameter
func createThing<T>(_ type: T.Type = T.self, _ block: (T) -> Void) {
// ...
// assuming sequence is create above
sequence.forEach(block)
}
enum MyEnum {
case a
case b
}
// a dedicated function for processing items also means better structured code :)
func processEnum(_ value: MyEnum) {
// do your stuff
}
// you can now pass only the second argument
createThing(processEnum)

Swift Generics: Cannot convert value of type to expected argument type

Here is my code:
protocol SomeProtocol {
}
class A: SomeProtocol {
}
func f1<T: SomeProtocol>(ofType: T.Type, listener: (T?) -> Void) {
}
func f2<T: SomeProtocol>(ofType: T.Type, listener: ([T]?) -> Void) {
}
func g() {
let l1: (SomeProtocol?) -> Void = ...
let l2: ([SomeProtocol]?) -> Void = ...
f1(ofType: A.self, listener: l1) // NO ERROR
f2(ofType: A.self, listener: l2) // COMPILE ERROR: Cannot convert value of type '([SomeProtocol]?) -> Void' to expected argument type '([_]?) -> Void'
}
What is the problem with the second closure having an argument of an array of generic type objects?
Swift 4.1 Update
This is a bug that was fixed in this pull request, which will make it into the release of Swift 4.1. Your code now compiles as expected in a 4.1 snapshot.
Pre Swift 4.1
This just looks like you're just stretching the compiler too far.
It can deal with conversions from arrays of sub-typed elements to arrays of super-typed elements, e.g [A] to [SomeProtocol] – this is covariance. It's worth noting that arrays have always been an edge case here, as arbitrary generics are invariant. Certain collections, such as Array, just get special treatment from the compiler allowing for covariance.
It can deal with conversions of functions with super-typed parameters to functions with sub-typed parameters, e.g (SomeProtocol) -> Void to (A) -> Void – this is contravariance.
However it appears that it currently cannot do both in one go (but really it should be able to; feel free to file a bug).
For what it's worth, this has nothing to do with generics, the following reproduces the same behaviour:
protocol SomeProtocol {}
class A : SomeProtocol {}
func f1(listener: (A) -> Void) {}
func f2(listener: ([A]) -> Void) {}
func f3(listener: () -> [SomeProtocol]) {}
func g() {
let l1: (SomeProtocol) -> Void = { _ in }
f1(listener: l1) // NO ERROR
let l2: ([SomeProtocol]) -> Void = { _ in }
f2(listener: l2)
// COMPILER ERROR: Cannot convert value of type '([SomeProtocol]) -> Void' to
// expected argument type '([A]) -> Void'
// it's the same story for function return types
let l3: () -> [A] = { [] }
f3(listener: l3)
// COMPILER ERROR: Cannot convert value of type '() -> [A]' to
// expected argument type '() -> [SomeProtocol]'
}
Until fixed, one solution in this case is to simply use a closure expression to act as a trampoline between the two function types:
// converting a ([SomeProtocol]) -> Void to a ([A]) -> Void.
// compiler infers closure expression to be of type ([A]) -> Void, and in the
// implementation, $0 gets implicitly converted from [A] to [SomeProtocol].
f2(listener: { l2($0) })
// converting a () -> [A] to a () -> [SomeProtocol].
// compiler infers closure expression to be of type () -> [SomeProtocol], and in the
// implementation, the result of l3 gets implicitly converted from [A] to [SomeProtocol]
f3(listener: { l3() })
And, applied to your code:
f2(ofType: A.self, listener: { l2($0) })
This works because the compiler infers the closure expression to be of type ([T]?) -> Void, which can be passed to f2. In the implementation of the closure, the compiler then performs an implicit conversion of $0 from [T]? to [SomeProtocol]?.
And, as Dominik is hinting at, this trampoline could also be done as an additional overload of f2:
func f2<T : SomeProtocol>(ofType type: T.Type, listener: ([SomeProtocol]?) -> Void) {
// pass a closure expression of type ([T]?) -> Void to the original f2, we then
// deal with the conversion from [T]? to [SomeProtocol]? in the closure.
// (and by "we", I mean the compiler, implicitly)
f2(ofType: type, listener: { (arr: [T]?) in listener(arr) })
}
Allowing you to once again call it as f2(ofType: A.self, listener: l2).
The listener closure in func f2<T: SomeProtocol>(ofType: T.Type, listener: ([T]?) -> Void) {...} requires its argument to be an array of T, where T is a type that implements SomeProtocol. By writing <T: SomeProtocol>, you are enforcing that all elements of that array are of the same type.
Say for example you have two classes: A and B. Both are completely distinct. Yet both implement SomeProtocol. In this case, the input array cannot be [A(), B()] because of the type constraint. The input array can either be [A(), A()] or [B(), B()].
But, when you define l2 as let l2: ([SomeProtocol]?) -> Void = ..., you allow the closure to accept an argument such as [A(), B()]. Hence this closure, and the closure you define in f2 are incompatible and the compiler cannot convert between the two.
Unfortunately, you cannot add type enforcement to a variable such as l2 as stated here. What you can do is if you know that l2 is going to work on arrays of class A, you could redefine it as follows:
let l2: ([A]?) -> Void = { ... }
Let me try and explain this with a simpler example. Let's say you write a generic function to find the greatest element in an array of comparables:
func greatest<T: Comparable>(array: [T]) -> T {
// return greatest element in the array
}
Now if you try calling that function like so:
let comparables: [Comparable] = [1, "hello"]
print(greatest(array: comparables))
The compiler will complain since there is no way to compare an Int and a String. What you must instead do is follows:
let comparables: [Int] = [1, 5, 2]
print(greatest(array: comparables))
Have nothing on Hamish's answer, he is 100% right. But if you wanna super simple solution without any explanation or code just work, when working with array of generics protocol, use this:
func f1<T: SomeProtocol>(ofType: T.Type, listener: (T?) -> Void) {
}
func f2<Z: SomeProtocol>(ofType: Z.Type, listener: ([SomeProtocol]?) -> Void) {
}

Swift generic function with clousure

I've the following generic function (not part of any class):
func execFuncWithGenericParameter<T, U>(f: (T) -> U){
print(f("Hello World"))
}
I'd like to call this function in with a closure like this:
execFuncWithGenericParameter(f: { (p: String) -> Int in
print(p)
return 4711
})
But the compiler (iPad Swift Playground) tells me that "(String) -> U is not convertible to (T) -> U".
Naturelly I've done done some investigation and was the opinion that the Compiler automatically will infer the types.
Thanks.
The types are being inferred as far as the nature of f: (T) -> U is concerned. The problem is the call to f inside your first method.
Ask yourself: what if T were not String? Then f("Hello world") would be illegal. That is the quandary you've left the compiler with — and it rightly refuses to deal with it.
Here's a legal version:
func execFuncWithGenericParameter<T,U>(f: ((T) -> U), param:T){
f(param)
}
execFuncWithGenericParameter(f: { (p: String) -> Int in
print(p)
return 4711
}, param:"Hello")
Now the first method knows that param will be a T, so all is well. And in the second method, when we call the first method, T is String and param is a String, so it compiles (and runs, and prints).

Infer closure return type from closure body when working with generics

I think best if I start with an example:
class Test<T> {
func test(closure: (T) -> Void) { }
func test(closure: (T) -> T) { }
func test(closure: (T) -> Test<T>) { }
}
Test<Int>().test { a in }
The code above gives the following error:
error: ambiguous use of 'test'
This is because the Swift compiler doesn't know to which one of the three methods is should map the call to. But from the closure body it's quite clear that it returns a Void, so it should pick the first method.
Looks like the Swift compiler cannot determine to which method overload to map the call to based on the closure body. If I explicitly specify the closure signature, then the problem goes away:
Test<Int>().test { (a: Int) -> Void in }
My question is: can I somehow instruct Swift to pick-up the correct overload for short-hand closure expressions like the one in discussion, or will I have to explicitly declare the closure signature?
Actually, it seems that I was pushing the compiler limits too hard (as #Martin R pointed in the comments). The { a in } closure was kinda incomplete, since the compiler had no statements to infer the closure return type from.
This works:
Test<Int>().test { (a: Int) -> Void in () }
Same as the following:
func doNothing() { }
Test<Int>().test { (a: Int) -> Void in doNothing() }
In the above examples the compiler is provided with the minimum amount of information to determine which overload to pick.