if let storedInE1 = e1 openas T { // T is the type of storedInE1, a copy of the value stored in e1
if let storedInE2 = e2 as? T { // Does e2 have type T? If so, copy its value to storedInE2
if storedInE1 == storedInE2 { ... } // Okay: storedInT1 and storedInE2 are both of type T, which we know is Equatable
}
}
What is the difference between e1 openas T and e2 as? T?
Or in pointfree notation: what is the difference between openas and as?/as!?
I believe what’s happening here is that the openas operation is creating some dynamic type T and once it’s binded the value’s dynamic type to a local type T then you can just use the regular cast operation to see if the other value is T.
Cool, now I also understand that part of the manifesto. And thank you @Karl for sharing the example. It makes more sense to me on how opening existentials could potentially work.
Opening existentials is not really a conditional operation, so associating it with if let is probably confusing as strawman syntax. Something like Karl's syntax makes more sense to me.
My own preference would be that existentials automatically open, and that we have some sort of syntax for expressing types relative to existential let bindings. So you could say:
let objects: Collection = ...
let destination: Collection = ...
if let destination = objects as? type(of: objects) { ... }
That would be cool, but it seems to only work well for comparing types which are exactly equal. If you’re checking multiple associated types, it becomes quite verbose and would require some kind of flow-tracking to keep track of which constraints you’ve tested for each existential:
let objects: Collection = ...
let destination: Collection = ...
if type(of: destination).Element == type(of: objects).Element {
// compiler needs to keep track of the above constraint
let sameContents = objects.elementsEqual(destination)
}
It is quite nice and approachable, though. It helps avoid some of the “angle-bracket blindness”.