An iteresting change.
You are mistaking one thing:
given the fact that bar's signature has two parameters, the compiler should determine that I want to use to second definition of
foo
, and print "--> 3".Your `bar` takes two parameters, but the function type of `fn` in your second foo does not.
In Swift (since SE-0066 Standardize function type argument syntax to require parentheses),
((B,C))->A is a function type taking a single argument of tuple type (B, C) and returning A
(B,C)->A is a function type taking two arguments of type A and type B and returning A
You can easily find the difference in a usual context:
You cannot assign your `bar` into a variable of type ((String, Int))->String.
var fn: ((String, Int))->String = bar //error: cannot convert value of type '(String, Int) -> String' to specified type '((String, Int)) -> String'
But you can into (String, Int)->String
var fn: (String, Int)->String = bar //compiles
A simple testing code:
func foo2<A, B>(_ type: A.Type,
arg: String? = nil,
_ fn: @escaping ((B))->A) {
print("--> 2")
}
func foo3<A, B, C>(_ type: A.Type,
arg: (String?, String)? = nil,
_ fn: @escaping ((B, C))->A) {
print("--> 3")
}
func foo2a<A, B>(_ type: A.Type,
arg: String? = nil,
_ fn: @escaping (B)->A) {
print("--> 2a")
}
func foo3a<A, B, C>(_ type: A.Type,
arg: (String?, String)? = nil,
_ fn: @escaping (B, C)->A) {
print("--> 3b")
}
foo2(String.self, bar) //10.1 error: cannot convert value of type '(String, Int) -> String' to expected argument type '((_)) -> _', compiles in Xcode 10.2
foo3(String.self, bar) //compiles both in 10.1 & 10.2
foo2a(String.self, bar) //compiles both in 10.1 & 10.2
foo3a(String.self, bar) //compiles both in 10.1 & 10.2
Only `foo2` shows different behaviors between 10.1 and 10.2.
Guessing from the error message, Swift 4.2 compiler (bundled with Xcode 10.1) seems to treat the function type `((B))->A` as:
a function type taking a single parameter of tuple type (B) and returning A.
But, as you know, there's no single element tuple in Swift. So, in my opinion, this is a bug of Swift 4.2.
With this bug, your `bar` is applicable only to `foo3`, so when you overload `foo` with `foo2` and `foo3`, the call cannot be ambiguous.
But in Swift 5 compiler, the bug is fixed and ((B))->A is treated as exactly the same as (B)->A. Your `bar` is applicable both to `foo2` and `foo3`.
So, overloading leads ambiguous call.
As you see, in some cases (though, not clearly documented), Swift converts a closure taking multiple parameters into a closure taking a single parameter of tuple type (or vice versa).
Please check SE-0110 Distinguish between single-tuple and multiple-argument function types, especially the linked article Additional Commentary.