72A12F4E 72A12F4E - 7 months ago 11
Swift Question

Strange behavior with type matching using generic closures

I get the following error with this Swift code in an Xcode 7.3 Playground.

Code

/**
* Represents a future value of type U, from a computation that has not been done yet.
*/
public struct Promise<T, U> {
var futureValue: T -> U

public func then<T,U,V>(closure: U -> V) -> Promise<T, V> {
return Promise<T, V>(futureValue: {
input in
let value = futureValue(input)
return closure(value)
})
}
}


Error

error: cannot invoke value of type 'T -> U' with argument list '((T))'
let value = futureValue(input)


This looks like it should work, because the types match up in my definition. What could be causing this behavior?

Answer

In

public func then<T,U,V>(closure: U -> V) -> Promise<T, V> { ... }

the local generic placeholders T, U hide (or shadow) the generic placeholders from the

public struct Promise<T, U> { ... }

type definition. Your method definition is equivalent to

public func then<T1,U1,V>(closure: U1 -> V) -> Promise<T1, V> {
    return Promise<T1, V>(futureValue: {
        input in
        let value = futureValue(input)
        return closure(value)
    })
}

which then shows the more understandable error message

error: cannot invoke value of type 'T -> U' with argument list '((T1))'

The solution is simple: Just omit T and U from the generic placeholder list in the method definition, these are inherited automatically from struct Promise. Note also that the futureValue property must be referenced via self inside the closure:

public func then<V>(closure: U -> V) -> Promise<T, V> {
    return Promise<T, V>(futureValue: {
        input in
        let value = self.futureValue(input)
        return closure(value)
    })
}