Enhanced existential types proposal discussion


(J.E. Schotsman) #1

Looks like great progress!

A few remarks:

1.I don't see the need for AllOf. A nested existential also acts as a constraint bundle, so you would have to write

Any<ProtocolA, AllOf< ProtocolB, ProtocolC>>

for consistency.

In a "is subclass of" constraint the superclass also acts as a constraint bundle, in a sense.

2.If openas never fails why do we need to use optional binding with it?

3. You have a typo in the "where clause" section: where Collection.Element == Streamable

···

On May 26, 2016, at 3:44 PM, Austin Zheng wrote:

The inimitable Joe Groff provided me with an outline as to how the design
could be improved. I've taken the liberty of rewriting parts of the
proposal to account for his advice.


(Austin Zheng) #2

The inimitable Joe Groff provided me with an outline as to how the design
could be improved. I've taken the liberty of rewriting parts of the
proposal to account for his advice.

Looks like great progress!

A few remarks:

1.I don't see the need for AllOf. A nested existential also acts as a constraint bundle, so you would have to write

Any<ProtocolA, AllOf< ProtocolB, ProtocolC>>

for consistency.

In a "is subclass of" constraint the superclass also acts as a constraint bundle, in a sense.

It was strawman syntax to allay some concerns were brought up earlier. At this point I think it makes the most sense to wait and see what happens to SE-0095, which will hopefully finalize some sort of existential syntax for Swift 3.x.

2.If openas never fails why do we need to use optional binding with it?

No good reason, to be honest. I stole the syntax from the Completing Generics document. The outermost 'openas' shouldn't ever fail, unless somehow additional requirements or tests (like a where clause) could be attached. However, I haven't come up with good syntax for such a case, though - "do x openas T { ... }" is almost as horrific.

3. You have a typo in the "where clause" section: where Collection.Element == Streamable

Oops, good catch. I'll fix that.

···

On May 26, 2016, at 6:51 AM, Jan E. Schotsman via swift-evolution <swift-evolution@swift.org> wrote:
On May 26, 2016, at 3:44 PM, Austin Zheng wrote:

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #3

The inimitable Joe Groff provided me with an outline as to how the design
could be improved. I've taken the liberty of rewriting parts of the
proposal to account for his advice.

Looks like great progress!

A few remarks:

1.I don't see the need for AllOf. A nested existential also acts as a constraint bundle, so you would have to write

Any<ProtocolA, AllOf< ProtocolB, ProtocolC>>

for consistency.

I’ve been thinking about this as well, especially in the context of Brent’s idea to distinguish existentials from generic constraints. My conclusion is that disallowing use of `Any` (or typealiases of `Any`) in generic constraints is an arbitrary restriction and introducing a new name `AllOf` for something that is structurally the same is unnecessary and potentially confusing.

I would like to see us eventually introduce a first class mechanism for abstracting constraints. That mechanism would become the preferred mechanism for factoring constraints, used in both generics and existentials. When we have that mechanism it might be reasonable to push people to use it rather than “existential constraints”. Maybe this can even happen along side enhanced existentials in Swift 4.

In a "is subclass of" constraint the superclass also acts as a constraint bundle, in a sense.

2.If openas never fails why do we need to use optional binding with it?

I assume you’re talking about this:

let e1: Equatable = ...
let e2: Equatable = ...

if let storedInE1 = e1 openas T {
  // is e2 also a T?
  if let storedInE2 = e2 as? T {
    // okay: storedInT1 and storedInE2 are both of type T, which we know is Equatable
    if storedInE1 == storedInE2 { ... }
  }
}

That is a good question. I imagine the purpose is to require a new scope to be introduced, inside which `T` is bound to the dynamic type of `e1` and `storedInE1` is bound to a value of that type. I am not sure why we need a new scope for these bindings rather than allowing them to be in effect in the surrounding scope (this may have been discussed already). If the new scope is necessary it might be worth elaborating on why. Otherwise, it would be simper if we could just do this:

let e1: Equatable = ...
let e2: Equatable = ...

let storedInE1 = e1 openas T
// is e2 also a T?
if let storedInE2 = e2 as? T {
  // okay: storedInT1 and storedInE2 are both of type T, which we know is Equatable
  if storedInE1 == storedInE2 { ... }
}

···

On May 26, 2016, at 8:51 AM, Jan E. Schotsman via swift-evolution <swift-evolution@swift.org> wrote:
On May 26, 2016, at 3:44 PM, Austin Zheng wrote:

3. You have a typo in the "where clause" section: where Collection.Element == Streamable

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution