I have asked a similar question before, but this is a much shorter and clearer version of it.
Carefully study this simple program:
(EDIT: On line 3, note that <T> and <T: Any> is the same thing, since "typealias Any = protocol <>" is a protocol composition of no protocols, which means it is constraining T with the constraint of no constraint.)
func foo<T: IntegerType>(v: T) { print(" Called foo<T: IntegerType>(\(v))") }
func foo<T: FloatingPointType>(v: T) { print(" Called foo<T: FloatingPointType>(\(v))") }
func foo<T: Any>(v: T) { print(" Called foo<T: Any>(\(v))") }
func bar<T, R>(v: T, fn: T -> R) -> R {
return fn(v)
}
func baz<T>(v: T) -> Void {
return foo(v)
}
print("foo-test:")
foo(111)
foo(1.1)
print("bar-test:")
bar(222, fn: foo)
bar(2.2, fn: foo)
print("baz-test:")
baz(333)
baz(3.3)
Here's the results (Xcode 7 beta 2):
foo-test:
Called foo<T: IntegerType>(111)
Called foo<T: FloatingPointType>(1.1)
bar-test:
Called foo<T: IntegerType>(222)
Called foo<T: FloatingPointType>(2.2)
baz-test:
Called foo<T: Any>(333)
Called foo<T: Any>(3.3)
To me, foo-test and bar-test behave as expected, but baz-test surprises me.
Is the output of baz-test different than the other two because of a compiler bug?
If no, please explain (in as much detail you can) why baz-test and bar-test produce different outputs.
Hi Jens,
The compiler is only looking at the function signature when doing static overload resolution. In your original example, in the case of
func bar<T, R>(v: T, fn: T -> R) -> R
called with
bar(222, fn: foo)
it chooses to resolve foo to
func foo<T: IntegerType>(v: T)
because it's the best match for the 'fn' parameter given that the first argument to bar() is an integer and both 'v' and the parameter to 'fn' have to have the same type.
In the case of baz<T>(v: T), when compiling the body of the function it determines the foo() to call based on what's known about T. In this case it's unconstrained, and the only foo that works here is foo<T:Any>(). The function is compiled independently of the context of any call, unlike a language like C++, where function templates are instantiated at each call site based on the types used in the call.
Hope this helps.
[edited to clarify why foo<T:IntegerType> is the best match]