I keep getting bitten by the restriction that generic types can only conform to a protocol in one way, and I think this happens to me because the language allows me to say things that are almost, but not quite, true.
I'm not making the classic faux-pas: if I have X<T> : P
I'm well aware that I'm not allowed to write a conditional extension of X
and change the way it conforms to P, so I don't even try to do that.
Instead, this comes up when I have constrained extensions of P
that provide default implementations. Here's an unrealistically-reduced example (I'm actually trying to do something useful):
protocol P {
static var isEquatable: Bool { get }
}
extension P {
static var isEquatable: Bool { false }
}
extension P where Self : Equatable {
static var isEquatable: Bool { true }
}
This code appears to say that if my P
-conforming type is Equatable
, it will, by default have isEquatable == true
. Almost anything I can do seems to confirm that idea.
func isEquatable<T: P>(_ t: T.Type) -> Bool { t.isEquatable }
extension Int : P {}
struct X : P {}
extension Array : P {}
// X is not equatable
assert(!X.isEquatable && !isEquatable(X.self))
// Int is equatable
assert(Int.isEquatable && isEquatable(Int.self))
// Arrays of non-equatable types are not equatable
assert(!Array<X>.isEquatable && !isEquatable(Array<X>.self))
// Arrays of equatable types are equatable
assert(Array<Int>.isEquatable)
But, I've misled myself.
// That property is not being used to satisfy the requirement.
assert(isEquatable(Array<Int>.self)) // Boom
In fact, it's really hard to explain what that simple bit of code at the top actually means, and for me that's a red flag. Try it yourself, and keep in mind that this works just fine, so it's not just about conditional conformances:
struct Y<T> {}
extension Y : P, Equatable where T: Equatable {
static func ==(_: Self, _: Self) -> Bool { true }
}
assert(Y<Int>.isEquatable)
assert(isEquatable(Y<Int>.self))
It seems to me that we should notâin the long runâallow simple code to mean something we can't explain simply, and that means breaking some source. What are your thoughts?