"Ambiguous Use" on Generic Method After Migration to Swift 4

How to call ambiguous generic function in Swift?

This is ambiguous because Int is both Hashable and Comparable, and neither of those two protocols are in the same hierarchy. (You can view the Int protocol hierarchy on Swifter.)

func f<T: Hashable>(t: T) {
println("Hashable: \(t)")
}
func f<T: Comparable>(t: T) {
println("Comparable: \(t)")
}

let number = 5
f(number)
// error: ambiguous use of 'f'

You can't explicitly tell it which function to call, because of the associated type requirements of each protocol, but what you can do is define a third function:

func f<T: Comparable where T: Hashable>(t: T) {
println("Both Hashable & Comparable: \(t)")
}
f(number)
// Both Hashable & Comparable: 5

This is how Swift implements the ..< operator, which otherwise would be ambiguous for types that implement both Comparable and ForwardIndexType.


To expand a little further, here's a look at what I meant by "you can't explicitly tell it which function to call, because of the associated type requirements of each protocol." Protocols can be used as types, as described in the Swift book chapter on Protocols:

protocol RandomNumberGenerator {
func random() -> Double
}

class Dice {
let generator: RandomNumberGenerator
// ...
}

In this example, the generator property can be any type that conforms to RandomNumberGenerator - similar to how id<ProtocolName> is used in Objective-C. However, protocols can only be used as types when they do not include an associated type or reference Self in their declaration. This unfortunately excludes nearly every built-in type in Swift, including Hashable and Comparable.

Hashable inherits from Equatable, which references Self when defining the == operator:

func ==(lhs: Self, rhs: Self) -> Bool

and Comparable does the same with its operators:

func <=(lhs: Self, rhs: Self) -> Bool
// similar definitions for <, >, and >=

These protocols can only be used as generic constraints, and not used as a type when declaring a variable. (As far as I can tell this is undocumented, but discoverable through error messages.)

Two protocols that don't have that restriction are Printable and BooleanType, so we can look at how they work. Bool is the only built-in type that conforms to BooleanType, and it is also Printable, so that will be our test type. Here are our generic functions p() and the variable t - note that, as before, we can't just call the function with t:

func p<T: Printable>(t: T) {
println("Printable: \(t)")
}
func p<T: BooleanType>(t: T) {
println("BooleanType: \(t)")
}

let t: Bool = true
p(t)
// error: Ambiguous use of 'p'

Instead, we need to cast (upcast?) t to a particular protocol using the as keyword, and call a particular generic function that way:

p(t as Printable)
// Printable: true

p(t as BooleanType)
// BooleanType: true

So as long as we have a qualifying protocol, we can choose which variant of the generic method to call.

Swift 4: Ambiguous Expression in reduce and generics

Got it to work by telling the compiler that the sequence elements (which is what ends up in the Zip2Sequence) also conform to Equatable:

func allItemsMatch<C1, C2>(_ s: C1, _ t: C2) -> Bool
where C1: Container, C2: Container,
C1.Item: Equatable, C1.Element: Equatable,
C1.Element == C2.Element, C1.Item == C2.Item,
C1: Sequence, C2: Sequence {
if s.count != t.count {
return false
}

return zip(s, t).reduce(true) { eq, tuple in
eq && (tuple.0 == tuple.1)
}
}


Related Topics



Leave a reply



Submit