Consider, this example:
protocol P {
var asHashable: AnyHashable { get }
}
extension P where Self: Hashable {
var asHashable: AnyHashable {
return AnyHashable(self)
}
}
struct A: P, Hashable {}
struct B: P, Hashable {}
let item1: P = A()
let item2: P = B()
let itemsEqual = item1.asHashable == item2.asHashable
let array1: [P] = [A(), B()]
let array2: [P] = [A(), B()]
let arraysEqual = array1 == array2
I want to work with items conforming to type P, but potentially of different types, so I cannot use generics, but existential containers save the day.
But also, I want to be able to compare such items, including items of different types. I cannot conform my root protocol to Hashable
, because that would break packing into existential container. After a bit of thinking I was able to solve this with a workaround above and now I can compare my items using asHashable
property.
That works acceptably when comparing individual items, but does not scale. Normally when you can compare type T
(it conforms to Equatable/Hashable) you also get comparison of [T]
and T?
for free.
But for my workaround that's not the case.
To enable compiler to synthesize conformance of [P]
and P?
to Equatable, I need to tell compiler that element type conforms to Equatable
.
Despite of syntax, element type is actually not a protocol. It is an existential container for protocol. [P]
stands for "array of existential containers for P".
I could trivially implement conformance of existential containers to Equatable
, but currently compiler does not allow me to express this. extension P
would mean protocol extension. There is no way in the language to express that I want to have an extension for existential container, rather than protocol.
Current approach it to have a custom struct that serves as a hand-written existential container and explicitly conforms to Equatable. But values need to be wrapped into existential container manually, which creates a lot of boilerplate code.
My problem would be solved if I could either:
- declarare extensions for existential container, something like:
extension *P {}
or
extension Existential where Value: P {}
- enable custom struct to be used a custom existential container allowing implicit conversion from values conforming to
P