Enhanced existential types proposal discussion

Yes, this is probably better founded.

Right now Swift doesn't have a blessed nothing type, the closest you can
get is enum Nothing { }, and that type doesn't participate in subtyping
relationships (it's not really a Bottom type like Scala's). Proposing such
an expansion to the type system is beyond the scope of this proposal,
although the "associated types" section should certainly be written such
that generalizing it would be trivial if Swift did get contravariant types
and a Bottom type.

Austin

···

On Mon, May 23, 2016 at 11:24 AM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 23.05.2016 um 18:08 schrieb Austin Zheng <austinzheng@gmail.com>:

I think that *all* methods should be available - at least in principle -
with associated types
- replaced by their upper bounds (i.e. Any if no constraints have been
given either by the protocol definition itself or th existential) if in
covariant position and
- replaced by their lower bounds if in contravariant position

As it is not possible to define lower bounds in Swift, the lower bounds
are always the bottom type (called `Nothing` in Swift and not be confused
with optionals). The bottom type has no members and therefore a method
referencing that type cannot be called and is effectively not available.

Thanks for the feedback! So methods that have associated types in
contravariant position would have those types be Nothing, unless there was
a concrete type bound to that associated type in the Any's where clause?

Example

protocol MyProtocol {
  associatedtype AssocType1
  associatedtype AssocType2
  func foo(x: AssocType1, y: AssocType2)
}

let a : Any<MyProtocol>
// on 'a', foo is exposed as 'foo(x: Nothing, y: Nothing)', and can thus
not be called

let b : Any<MyProtocol where .AssocType1 == Int>
// on 'b', foo is exposed as 'foo(x: Int, y: Nothing)' and still can't be
called

let c : Any<MyProtocol where .AssocType1 == Int, .AssocType2 == String>
// on 'c', foo is exposed as 'foo(x: Int, y: String)', and can therefore
be called

Let me know if this is what you had in mind.

Yes, that’s what I had in mind.

IMHO this would make for simple rules and if Swift might one day get the
ability to specify lower bounds this would easily fit in (I don’t know how
useful that would be in reality, but I think Scala allows the declaration
of lower bounds).

-Thorsten

I agree; the difference between protocols with and without associated types has been an endless source of confusion for a lot of people.

Speaking of which, for those who care I rewrote the draft proposal to attempt a much more rigorous treatment of the semantics of the generalized existential, including a discussion about existential type equivalence and subtyping. It would be nice to see people poke holes in my logic so I can patch them up. https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

I think that *all* methods should be available - at least in principle - with associated types
- replaced by their upper bounds (i.e. Any if no constraints have been given either by the protocol definition itself or th existential) if in covariant position and
- replaced by their lower bounds if in contravariant position

As it is not possible to define lower bounds in Swift, the lower bounds are always the bottom type (called `Nothing` in Swift and not be confused with optionals). The bottom type has no members and therefore a method referencing that type cannot be called and is effectively not available.

Called `Nothing` in Swift? Where do you get that? `func foo(s: Nothing) {}` gives me “use of undeclared type `Nothing`”. If Swift had a bottom type wouldn’t we be able to declare a function accepting an argument of type `Nothing` (we could just never call it because we couldn’t construct an argument).

oops, sorry, I had wanted to type „called `Nothing` in Scala“ :-)

Lol that makes more sense. :)

···

Sent from my iPad

On May 23, 2016, at 1:21 PM, Thorsten Seitz <tseitz42@icloud.com> wrote:

Am 23.05.2016 um 19:17 schrieb Matthew Johnson <matthew@anandabits.com>:
On May 23, 2016, at 10:57 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Am 23.05.2016 um 00:18 schrieb Austin Zheng via swift-evolution <swift-evolution@swift.org>:

-Thorsten

-Thorsten

Austin

On May 22, 2016, at 3:05 PM, Russ Bishop via swift-evolution <swift-evolution@swift.org> wrote:

On May 17, 2016, at 1:55 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

I agree with this. If we're certain we should reskin protocol<> as Any<>, we should frontload that change—in addition to affecting source code, it'd also influence the runtime behavior of type printing/parsing, which can't be statically migrated in the future. I think any discussion of extending existentials has to be considered out of scope for Swift 3, though, so the Any rename deserves its own proposal.

-Joe

Its really unfortunate that the generics work is probably going to be deferred. When you really dive in to protocol-oriented programming and designing frameworks to be native Swift (taking advantage of Swift features) the existential problem comes up a lot and leads to sub-optimal designs, abandonment of type safety, or gobs of boilerplate.

Russ

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

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

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

One initial bit of feedback - I believe if you have existential types, I believe you can define Sequence Element directly, rather than with a type alias. e.g.

protocol Sequence {
associatedtype Element
associatedtype Iterator: any<IteratorProtocol where IteratorProtocol.Element==Element>
associatedtype SubSequence: any<Sequence where Sequence.Element == Element>

}

That's not really the same thing. Any<IteratorProtocol> is an existential, not a protocol. It's basically an automatically-generated version of our current `AnyIterator<T>` type (though with some additional flexibility). It can't appear on the right side of a `:`, any more than AnyIterator could.

After this proposal you should be able to use these existentials anywhere you can place a constraint, so it would work. You can do this with the protocol composition operator today and the future existential is just an extension of that capability.

···

Sent from my iPad
On May 23, 2016, at 9:52 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

What *would* work is allowing `where` clauses on associated types:

protocol Sequence {
associatedtype Element
associatedtype Iterator: IteratorProtocol where Iterator.Element==Element
associatedtype SubSequence: Sequence where SubSequence.Element == Element

}

I believe this is part of the generics manifesto.

--
Brent Royal-Gordon
Architechies

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

It's actually a proposal waiting to be merged: https://github.com/apple/swift-evolution/pull/284

···

On 24 May 2016, at 04:52, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

One initial bit of feedback - I believe if you have existential types, I believe you can define Sequence Element directly, rather than with a type alias. e.g.

protocol Sequence {
associatedtype Element
associatedtype Iterator: any<IteratorProtocol where IteratorProtocol.Element==Element>
associatedtype SubSequence: any<Sequence where Sequence.Element == Element>

}

That's not really the same thing. Any<IteratorProtocol> is an existential, not a protocol. It's basically an automatically-generated version of our current `AnyIterator<T>` type (though with some additional flexibility). It can't appear on the right side of a `:`, any more than AnyIterator could.

What *would* work is allowing `where` clauses on associated types:

protocol Sequence {
associatedtype Element
associatedtype Iterator: IteratorProtocol where Iterator.Element==Element
associatedtype SubSequence: Sequence where SubSequence.Element == Element

}

I believe this is part of the generics manifesto.

--
Brent Royal-Gordon
Architechies

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

Thank you for your feedback, Doug! I appreciate you taking the time to read
through everything and write up your thoughts. I'll revise the proposal and
plan for a 2017 time frame.

I would also be happy to see someone from the core team write up a proposal
when the time is right, should they feel this one isn't in the spirit of
the desired feature.

Austin

···

On Sun, Jun 5, 2016 at 4:20 PM, Douglas Gregor <dgregor@apple.com> wrote:

On May 18, 2016, at 12:35 AM, Austin Zheng <austinzheng@gmail.com> wrote:

I've put together a considerably more detailed draft proposal, taking into
account as much of Matthew's feedback as I could. You can find it below:

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

Since there is no chance this will come up for review anytime soon, I
expect to make significant revisions to it over the next month or so. Any
feedback would be greatly appreciated.

This is very much Swift 4 territory, but I can’t help myself… so…

The actual feature description is spread out through this very long
document, with user-facing ideas (e.g., using “anonymous associated types”)
intermixed with deeper technical details (existential type equivalence), so
it’s very daunting to read. Please bring the user-facing features to the
front (“Proposed Solution”) with examples, and save the deeper technical
details for “Detailed Design”. You want more readers to make it through the
part that affects them.

Shortcut 'dot' notation: If there is only one protocol with associated
types specified in the requirements, and there are no nested Any<...> requirements
with where clauses of their own, that protocol's name can be omitted from
the whereclause constraints:

// Okay// Would otherwise be Any< ~ where Collection.Element == Int>let a : Any<class, Collection, Any<Streamable, CustomStringConvertible> where .Element == Int>
// NOT ALLOWED// Both Collection and OptionSetType have associated types.let b : Any<Collection, OptionSetType where .Element == Int>

FWIW, I think “.Element == Int” should be the only syntax. In generic
signatures, if you have two different protocols with same-named associated
types, and a given type parameter (or associated type) conforms to both
protocols, the associated types are (implicitly) made equivalent via an
inferred same-type constraint. So there’s no reason to introduce the
“Collection.Element == Int” syntax, because the “Collection” part is
basically irrelevant.

Once existentials have been suitably enhanced, there is a strong analogy
between an existential and a generic signature with a single type parameter
that you can’t name. An existential Any<Collection where .Element :
> has most of the same characteristics as a generic something with
the signature <T : Collection where T.Element : Equatable>. Specifically,
the sections on “Existential type equivalence”, “Ordering”, “Real types to
anonymous associated types”, “Anonymous associated types to real types”.
could be reduced to a few small, simple examples and a mention of the
analogous behavior of generics. It will be far easier to explain this way,
and readers don’t need to get immersed in the details. Where there are
differences vs. generics, that’s important to point out.

“Associated typealias rewriting”: this also falls out of the equivalence
with generics + SE-0092.

“Associated types and member exposure”: you don’t make the point that it
only makes sense to refer to the associated types of a *let* constant; a
*var* could change its type dynamically, which would invalidate the
typing rules. Did you consider just using “x.dynamicType” in the type
grammar for this? It’s more general, in that you can refer to associated
types but also talk about the dynamic type of “x” itself, e.g.,

let x: Equatable = …
let y: Equatable = …
if let yAsX = y as? x.dynamicType { … x == yAsX … }

which is (almost?) as powerful as a general “open” expression.

I’m not a fan of the “anonymous associated types” terminology: these are
associated types of a type of some runtime-defined value. The only thing
“anonymous” about them is that it’s harder to spell the base type;
otherwise, they’re just like associated types of a generic type parameter.
Again, the generics analogy is strong here.

FWIW, I don’t think we’ll ever need “opening existentials” with what
you’ve described here. Also, remember that a method of a protocol extension
essentially opens “Self”, so we already have one way to open an existential
(and that’s probably enough).

I was a little surprised you didn’t point out that AnyObject could become

typealias AnyObject = Any<class>

or give the nice “AnyCollection” syntax:

typealias AnyCollection<T> = Any<Collection where .Element == T>

the latter of which is fairly important, because it gives nice syntactic
sure to one of the most highly-requested features [*]. I’d suggest having
that example very, very early.

- Doug

[*] That generally comes in as “Swift should have parameterized protocols…”

Austin

On Tue, May 17, 2016 at 9:52 PM, Austin Zheng <austinzheng@gmail.com> > wrote:

On Tue, May 17, 2016 at 1:25 PM, Matthew Johnson <matthew@anandabits.com> >> wrote:

Within the angle brackets are zero or more 'clauses'. Clauses are
separated by semicolons. (This is so commas can be used in where
constraints, below. Better ideas are welcome. Maybe it's not necessary; we
can use commas exclusively.)

I’m not a fan of the semicolon idea. I don’t see any reason for this.
The `where` keyword separates the protocol list from the constraints just
fine. The list on either side should be able to use commas with no problem
(or line breaks if that proposal goes through).

I'm leaning towards getting rid of the commas, but would like to write
out a few 'dummy' examples to see if there are any readability issues that
arise.

Replaced with what? Whitespace separation? I suppose that might work
for the protocol list but it feels inconsistent with the rest of Swift.
Commas plus (hopefully) the alternative of newline seem like the right
direction to me.

Sorry, I completely misspoke (mistyped?). I meant I want to get rid of
the semicolons and use commas. I've come to the conclusion that there are
no readability issues, protocol<> already uses commas, and semicolons used
in this manner don't have a precedent anywhere else in the language.

There are five different possible clauses:

   - 'class'. Must be the first clause, if present. Places a
   constraint on the existential to be any class type. (Implies: Only one can
   exist. Mutually exclusive with class name clause.)

(In the future a follow-up proposal should add in 'struct' or 'value'
as a counterpart.)

If we’re going to allow `struct` we should also allow `enum`. `value`
would allow either of those.

Of course. A future proposal can allow list members to discuss the exact
details as to how struct, value, or enum specifiers should work.

Yep, agree. Just mentioning that if we’re going to reference it we
should not leave obvious holes in what would be considered. :)

Absolutely.

   - Class name. Must be the first clause, if present. (Implies: Only
   one can exist. Mutually exclusive with 'class'.) Places a constraint on the
   existential (not really an existential anymore) to be an instance of the
   class, or one of its subclasses.

It is still be an existential if it includes protocol requirements that
the class does not fulfill. For example, you might have Any<UIView,
> where UIView does not conform to SomeProtocol, but various
subclasses do.

Fair enough. (I don't think the way things work would be affected.)

Your proposal doesn’t discuss composing Any in the way that Adrian’s
did like this:

typealias Foo = Any<SomeClass, SomeProtocol, OtherProtocol>
Any<AnotherProtocol, Foo>

I didn't think it needed to be discussed. An Any<...> existential type
is a type 'expression' just like any other, and should be allowed to
participate in other Any<...>s.

I like the idea of composition as it allows us to factor out
constraints. If we are going to do that we should allow a class to be
specified in the composition as long is it is a subclass of all class
requirements of Any types it composes. For example, this should be allowed:

typealias Bar = Any<SubclassOfSomeClass, Foo, AnotherProtocol>

This is still one class requirement for Bar, it just refines the class
requirement of Foo to be SubclassOfSomeClass rather than just SomeClass.

This is a good point. There should be clarification as to how special
cases of Any<...> used in another Any<...> behave. For example, like you
said Any<MyClass, Any<SomeSubclassOfMyClass, Protocol>> should be valid.
This will go into any proposal that emerges from the discussion.

Yes, this is why we need to discuss Any composition. There are also
cases of incompatible associated type constraints which need to be rejected
(such as composing two Any’s where one has Element == String and another
has Element == Int).

Example: Any<UIViewController; UITableViewDataSource;
>
"Any UIViewController or subclass which also satisfies the table view
data source and delegate protocols"

   - Dynamic protocol. This is entirely composed of the name of a
   protocol which has no associated types or Self requirement.

Example: Any<CustomStringConvertible; BooleanType>
"Any type which conforms to both the CustomStringConvertible and
BooleanType protocols"

I'm going to use 'static protocol' to refer to a protocol with
associated types or self requirements. Feel free to propose a more sound
name.

   - Self-contained static protocol, simple. This is composed of the
   name of a static protocol, optionally followed by a 'where' clause in which
   the associated types can be constrained (with any of the three basic
   conformance types: subclassing, protocol conformance, or type equality).
   Associated types are referred to with a leading dot.

Please do not introduce terms “dynamic protocol” and “static
protocol”. We want to support existentials of protocols that have self or
associated type requirements. The dynamic vs static distinction is a
limitation of the current implementation of Swift and doesn’t make sense
for the long term vision.

I'm not trying to introduce new terms, these are just placeholders. At
the same time "protocols with self or associated type requirements" is
cumbersome to work with and it would be nice for someone to come up with a
descriptive term of art for referring to them.

I agree that a better term would be useful. In the meantime, I would
prefer something like “trivial” and “nontrivial” protocols.

I've decided to just use the full name until the community comes up with
better names. Clarity is preferable to brevity in this case.

Example: Any<Collection where .Generator.Element : NSObject,
.Generator.Element : SomeProtocol>
"Any type that is a Collection, whose elements are NSObjects or their
subclasses conforming to SomeProtocol.”

Swift does not allow disjunction of requirements. Only conjunctions
are supported. That means the correct reading is:

"Any type that is a Collection, whose elements are NSObjects *and* their
subclasses conforming to SomeProtocol.”

Yes, that is what I meant. "whose elements are (NSObjects or their
subclasses) conforming to SomeProtocol”.

Ok, good. Wasn’t quite clear to me.

Yes, the verbiage will need to be clearer in the future. That sentence
could be ambiguously parsed.

   - Bound static protocol. This is the same as a self-contained
   static protocol, but with a leading "<name> as " which binds the protocol
   to a generic typealias. The name can be then be used in subsequent clauses
   to build constraints.

Example: Any<T as Collection; IntegerLiteralConvertible where
.IntegerLiteralType == T.Element>.
"Any type that is a Collection, and also can be built from an integer
literal, in which the collection elements are the same type as the type of
the integer used for the integer literal conformance.”

I’m not sure about this, but if we’re going to do it it should be the
other way around: `Collection as T` with the alias *after* the name of
the protocol.

I like this, it flows better. "Protocol as T where Protocol.Foo == Int,
Protocol.Bar : Baz”.

Why did you introduce an alias here and then not use it? Did you mean
"Protocol as T where T.Foo == Int, T.Bar : Baz"

Another result of rushing to compose an email. Sorry!

You are also using “dot shorthand” here to refer to an associated type
of IntegerLiteralConvertible. I think “dot shorthand” should be limited to
cases where there is only one protocol that is getting constrained. In
other cases, we need to be clear about which protocol we are referring to.

I borrowed dot shorthand from the generics manifesto. But you are right,
it should only be allowed if there is one protocol with associated types or
self requirements clause in the Any<...> construction.

I would actually go further and limit it to one protocol period, and
possibly even to one protocol and no type names (as types can have nested
types and typealiases). When we allow shorthand it should be immediately
unambiguous what the shorthand references with no need to look at type or
protocol declarations.

It might be desirable to propose the proposal with no allowance for
shorthand, and have the dot shorthand be a smaller follow-up proposal.

There will be rules to prevent recursive nesting. For example, if
generic typealiases are allowed, they cannot refer to each other in a
circular manner (like how structs can't contain themeselves, and you can't
create a cyclic graph of enums containing themselves).

How an existential can be used depends on what guarantees are provided
by the clauses. For example, 'Any<Equatable>' can't be used for much; if
there were any methods on Equatable that did not use the associated types
at all you'd be able to call them, but that's about it. However,
'Any<Equatable where .Self == String>' would allow for == to be called on
instances. (This is a stupid example, since Any<Equatable where .Self ==
> is equivalent to 'String', but there are almost certainly useful
examples one could come up with.)

In order of increasing 'power':

   - Don't constrain any associated types. You can pass around
   Any<Equatable>s, but that's about it.
   - Constrain associated types to conform to protocols.
   - Fully constrain associated types.

I think we need to spell out pretty clearly what members we expect to
be available or not available. This section probably needs the most design
and elaboration.

For example, we probably can’t access a member who uses an associated
type as an input unless it is constrained to a specific type. On the other
hand output types probably don’t need to limit access to a member.
However, if the output type is Self or an associated type the visible
signature would have an output type which has the relevant constraints of
the existential applied, but no more. In some cases this means the output
type would simply be Any.

Absolutely. This is vaguely what I had in mind but I wanted to get
something down first. Thanks for thinking through some of the implications
:).

That’s what I thought. Just wanted to start the process of elaborating
expectations.

Where this really gets tricky is for compound types like functions,
generic types, etc. Working out the details in these cases is pretty
complex. I will defer to Doug on whether it is best to just defer those
cases to the future, leave them up to the implementer, or try to work out
all of the relevant details in the proposal (in which case we probably need
a type system expert to help!).

Yes, exactly! For example, can Any<...> existentials involving protocols
with associated types or self requirements be used within generic function
or type definitions? Maybe there's an argument that existential types of
this nature are redundant if you have access to generics (e.g. defining a
property on a generic type that is a Collection containing Ints; you should
be able to do that today). On the other hand, maybe there are use cases I
haven't thought of…

I see no reason they shouldn’t be. They are not redundant at all. For
example, you may want to store instances in a heterogeneous collection.
You need existentials to do that.

A simple example of what I was referring to there is something like this:

protocol P {
    associatedtype Foo

    func bar(callback: (Foo) -> ())
}

In other words, types in the signature of a protocol member are complex
types that reference Self or associated types. I think you really need a
formal understanding of the type system to understand how to expose these
members through a constrained existential. We can probably understand the
expected behavior in some of the simpler cases on a case by case basis, but
that approach doesn’t scale at all and is arbitrary. If they’re going to
be supported an expert is going to need to be involved in the design.

Yes. I have some ideas regarding this topic.

One area you didn’t touch on is “opening” the existential? Is that out
of scope for this proposal? That would be fine with me as this proposal is
already taking on a lot. But if so, you should mention something about
future directions as it is pretty closely related to this proposal.

Yes, existential opening is explicitly separate from this (although I
wanted to mention it in the section where I talk about how Any<Equatable>
is not very useful). But you are absolutely right, this proposal should
discuss how it wants to interact with possible future directions.

Another area you didn’t touch on is whether Any constructs (and
typealiases referring to them) should be usable as generic constraints. I
would expect this to be possible but I think we need to spell it out.

I'm hoping for community input. This is a tricky subject, and at some
point we'll bump into implementation limitations.

I don’t think it’s too tricky. You can just unpack the constraints of
the Any into the list of generic constraints. Maybe I’m missing something,
but I don’t think so.

-Matthew

I've put together a considerably more detailed draft proposal, taking into account as much of Matthew's feedback as I could. You can find it below:

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

Since there is no chance this will come up for review anytime soon, I expect to make significant revisions to it over the next month or so. Any feedback would be greatly appreciated.

This is very much Swift 4 territory, but I can’t help myself… so…

The actual feature description is spread out through this very long document, with user-facing ideas (e.g., using “anonymous associated types”) intermixed with deeper technical details (existential type equivalence), so it’s very daunting to read. Please bring the user-facing features to the front (“Proposed Solution”) with examples, and save the deeper technical details for “Detailed Design”. You want more readers to make it through the part that affects them.

Shortcut 'dot' notation: If there is only one protocol with associated types specified in the requirements, and there are no nested Any<...> requirements with where clauses of their own, that protocol's name can be omitted from the whereclause constraints:

// Okay
// Would otherwise be Any< ~ where Collection.Element == Int>
let a : Any<class, Collection, Any<Streamable, CustomStringConvertible> where .Element == Int>

// NOT ALLOWED
// Both Collection and OptionSetType have associated types.
let b : Any<Collection, OptionSetType where .Element == Int>
FWIW, I think “.Element == Int” should be the only syntax. In generic signatures, if you have two different protocols with same-named associated types, and a given type parameter (or associated type) conforms to both protocols, the associated types are (implicitly) made equivalent via an inferred same-type constraint. So there’s no reason to introduce the “Collection.Element == Int” syntax, because the “Collection” part is basically irrelevant.

Once existentials have been suitably enhanced, there is a strong analogy between an existential and a generic signature with a single type parameter that you can’t name. An existential Any<Collection where .Element : Equatable> has most of the same characteristics as a generic something with the signature <T : Collection where T.Element : Equatable>. Specifically, the sections on “Existential type equivalence”, “Ordering”, “Real types to anonymous associated types”, “Anonymous associated types to real types”. could be reduced to a few small, simple examples and a mention of the analogous behavior of generics. It will be far easier to explain this way, and readers don’t need to get immersed in the details. Where there are differences vs. generics, that’s important to point out.

“Associated typealias rewriting”: this also falls out of the equivalence with generics + SE-0092.

“Associated types and member exposure”: you don’t make the point that it only makes sense to refer to the associated types of a let constant; a var could change its type dynamically, which would invalidate the typing rules. Did you consider just using “x.dynamicType” in the type grammar for this? It’s more general, in that you can refer to associated types but also talk about the dynamic type of “x” itself, e.g.,

  let x: Equatable = …
  let y: Equatable = …
  if let yAsX = y as? x.dynamicType { … x == yAsX … }

which is (almost?) as powerful as a general “open” expression.

I’m not a fan of the “anonymous associated types” terminology: these are associated types of a type of some runtime-defined value. The only thing “anonymous” about them is that it’s harder to spell the base type; otherwise, they’re just like associated types of a generic type parameter. Again, the generics analogy is strong here.

FWIW, I don’t think we’ll ever need “opening existentials” with what you’ve described here. Also, remember that a method of a protocol extension essentially opens “Self”, so we already have one way to open an existential (and that’s probably enough).

I was a little surprised you didn’t point out that AnyObject could become

  typealias AnyObject = Any<class>

or give the nice “AnyCollection” syntax:

  typealias AnyCollection<T> = Any<Collection where .Element == T>

the latter of which is fairly important, because it gives nice syntactic sure to one of the most highly-requested features [*]. I’d suggest having that example very, very early.

  - Doug

[*] That generally comes in as “Swift should have parameterized protocols…”

Great feedback here Doug.

FWIW, we also occasionally get "Swift should have parameterized protocols" in the context of multiple conformances by the same concrete type (as in things like ConvertibleTo<T> protocol).

···

Sent from my iPad

On Jun 5, 2016, at 6:20 PM, Douglas Gregor <dgregor@apple.com> wrote:

On May 18, 2016, at 12:35 AM, Austin Zheng <austinzheng@gmail.com> wrote:

Austin

On Tue, May 17, 2016 at 9:52 PM, Austin Zheng <austinzheng@gmail.com> wrote:

On Tue, May 17, 2016 at 1:25 PM, Matthew Johnson <matthew@anandabits.com> wrote:

Within the angle brackets are zero or more 'clauses'. Clauses are separated by semicolons. (This is so commas can be used in where constraints, below. Better ideas are welcome. Maybe it's not necessary; we can use commas exclusively.)

I’m not a fan of the semicolon idea. I don’t see any reason for this. The `where` keyword separates the protocol list from the constraints just fine. The list on either side should be able to use commas with no problem (or line breaks if that proposal goes through).

I'm leaning towards getting rid of the commas, but would like to write out a few 'dummy' examples to see if there are any readability issues that arise.

Replaced with what? Whitespace separation? I suppose that might work for the protocol list but it feels inconsistent with the rest of Swift. Commas plus (hopefully) the alternative of newline seem like the right direction to me.

Sorry, I completely misspoke (mistyped?). I meant I want to get rid of the semicolons and use commas. I've come to the conclusion that there are no readability issues, protocol<> already uses commas, and semicolons used in this manner don't have a precedent anywhere else in the language.

There are five different possible clauses:

'class'. Must be the first clause, if present. Places a constraint on the existential to be any class type. (Implies: Only one can exist. Mutually exclusive with class name clause.)

(In the future a follow-up proposal should add in 'struct' or 'value' as a counterpart.)

If we’re going to allow `struct` we should also allow `enum`. `value` would allow either of those.

Of course. A future proposal can allow list members to discuss the exact details as to how struct, value, or enum specifiers should work.

Yep, agree. Just mentioning that if we’re going to reference it we should not leave obvious holes in what would be considered. :)

Absolutely.

Class name. Must be the first clause, if present. (Implies: Only one can exist. Mutually exclusive with 'class'.) Places a constraint on the existential (not really an existential anymore) to be an instance of the class, or one of its subclasses.

It is still be an existential if it includes protocol requirements that the class does not fulfill. For example, you might have Any<UIView, SomeProtocol> where UIView does not conform to SomeProtocol, but various subclasses do.

Fair enough. (I don't think the way things work would be affected.)

Your proposal doesn’t discuss composing Any in the way that Adrian’s did like this:

typealias Foo = Any<SomeClass, SomeProtocol, OtherProtocol>
Any<AnotherProtocol, Foo>

I didn't think it needed to be discussed. An Any<...> existential type is a type 'expression' just like any other, and should be allowed to participate in other Any<...>s.

I like the idea of composition as it allows us to factor out constraints. If we are going to do that we should allow a class to be specified in the composition as long is it is a subclass of all class requirements of Any types it composes. For example, this should be allowed:

typealias Bar = Any<SubclassOfSomeClass, Foo, AnotherProtocol>

This is still one class requirement for Bar, it just refines the class requirement of Foo to be SubclassOfSomeClass rather than just SomeClass.

This is a good point. There should be clarification as to how special cases of Any<...> used in another Any<...> behave. For example, like you said Any<MyClass, Any<SomeSubclassOfMyClass, Protocol>> should be valid. This will go into any proposal that emerges from the discussion.

Yes, this is why we need to discuss Any composition. There are also cases of incompatible associated type constraints which need to be rejected (such as composing two Any’s where one has Element == String and another has Element == Int).

Example: Any<UIViewController; UITableViewDataSource; UITableViewDelegate>
"Any UIViewController or subclass which also satisfies the table view data source and delegate protocols"
Dynamic protocol. This is entirely composed of the name of a protocol which has no associated types or Self requirement.
Example: Any<CustomStringConvertible; BooleanType>
"Any type which conforms to both the CustomStringConvertible and BooleanType protocols"

I'm going to use 'static protocol' to refer to a protocol with associated types or self requirements. Feel free to propose a more sound name.

Self-contained static protocol, simple. This is composed of the name of a static protocol, optionally followed by a 'where' clause in which the associated types can be constrained (with any of the three basic conformance types: subclassing, protocol conformance, or type equality). Associated types are referred to with a leading dot.

Please do not introduce terms “dynamic protocol” and “static protocol”. We want to support existentials of protocols that have self or associated type requirements. The dynamic vs static distinction is a limitation of the current implementation of Swift and doesn’t make sense for the long term vision.

I'm not trying to introduce new terms, these are just placeholders. At the same time "protocols with self or associated type requirements" is cumbersome to work with and it would be nice for someone to come up with a descriptive term of art for referring to them.

I agree that a better term would be useful. In the meantime, I would prefer something like “trivial” and “nontrivial” protocols.

I've decided to just use the full name until the community comes up with better names. Clarity is preferable to brevity in this case.

Example: Any<Collection where .Generator.Element : NSObject, .Generator.Element : SomeProtocol>
"Any type that is a Collection, whose elements are NSObjects or their subclasses conforming to SomeProtocol.”

Swift does not allow disjunction of requirements. Only conjunctions are supported. That means the correct reading is:

"Any type that is a Collection, whose elements are NSObjects and their subclasses conforming to SomeProtocol.”

Yes, that is what I meant. "whose elements are (NSObjects or their subclasses) conforming to SomeProtocol”.

Ok, good. Wasn’t quite clear to me.

Yes, the verbiage will need to be clearer in the future. That sentence could be ambiguously parsed.

Bound static protocol. This is the same as a self-contained static protocol, but with a leading "<name> as " which binds the protocol to a generic typealias. The name can be then be used in subsequent clauses to build constraints.

Example: Any<T as Collection; IntegerLiteralConvertible where .IntegerLiteralType == T.Element>.
"Any type that is a Collection, and also can be built from an integer literal, in which the collection elements are the same type as the type of the integer used for the integer literal conformance.”

I’m not sure about this, but if we’re going to do it it should be the other way around: `Collection as T` with the alias after the name of the protocol.

I like this, it flows better. "Protocol as T where Protocol.Foo == Int, Protocol.Bar : Baz”.

Why did you introduce an alias here and then not use it? Did you mean "Protocol as T where T.Foo == Int, T.Bar : Baz"

Another result of rushing to compose an email. Sorry!

You are also using “dot shorthand” here to refer to an associated type of IntegerLiteralConvertible. I think “dot shorthand” should be limited to cases where there is only one protocol that is getting constrained. In other cases, we need to be clear about which protocol we are referring to.

I borrowed dot shorthand from the generics manifesto. But you are right, it should only be allowed if there is one protocol with associated types or self requirements clause in the Any<...> construction.

I would actually go further and limit it to one protocol period, and possibly even to one protocol and no type names (as types can have nested types and typealiases). When we allow shorthand it should be immediately unambiguous what the shorthand references with no need to look at type or protocol declarations.

It might be desirable to propose the proposal with no allowance for shorthand, and have the dot shorthand be a smaller follow-up proposal.

There will be rules to prevent recursive nesting. For example, if generic typealiases are allowed, they cannot refer to each other in a circular manner (like how structs can't contain themeselves, and you can't create a cyclic graph of enums containing themselves).

How an existential can be used depends on what guarantees are provided by the clauses. For example, 'Any<Equatable>' can't be used for much; if there were any methods on Equatable that did not use the associated types at all you'd be able to call them, but that's about it. However, 'Any<Equatable where .Self == String>' would allow for == to be called on instances. (This is a stupid example, since Any<Equatable where .Self == String> is equivalent to 'String', but there are almost certainly useful examples one could come up with.)

In order of increasing 'power':
Don't constrain any associated types. You can pass around Any<Equatable>s, but that's about it.
Constrain associated types to conform to protocols.
Fully constrain associated types.

I think we need to spell out pretty clearly what members we expect to be available or not available. This section probably needs the most design and elaboration.

For example, we probably can’t access a member who uses an associated type as an input unless it is constrained to a specific type. On the other hand output types probably don’t need to limit access to a member. However, if the output type is Self or an associated type the visible signature would have an output type which has the relevant constraints of the existential applied, but no more. In some cases this means the output type would simply be Any.

Absolutely. This is vaguely what I had in mind but I wanted to get something down first. Thanks for thinking through some of the implications :).

That’s what I thought. Just wanted to start the process of elaborating expectations.

Where this really gets tricky is for compound types like functions, generic types, etc. Working out the details in these cases is pretty complex. I will defer to Doug on whether it is best to just defer those cases to the future, leave them up to the implementer, or try to work out all of the relevant details in the proposal (in which case we probably need a type system expert to help!).

Yes, exactly! For example, can Any<...> existentials involving protocols with associated types or self requirements be used within generic function or type definitions? Maybe there's an argument that existential types of this nature are redundant if you have access to generics (e.g. defining a property on a generic type that is a Collection containing Ints; you should be able to do that today). On the other hand, maybe there are use cases I haven't thought of…

I see no reason they shouldn’t be. They are not redundant at all. For example, you may want to store instances in a heterogeneous collection. You need existentials to do that.

A simple example of what I was referring to there is something like this:

protocol P {
    associatedtype Foo

    func bar(callback: (Foo) -> ())
}

In other words, types in the signature of a protocol member are complex types that reference Self or associated types. I think you really need a formal understanding of the type system to understand how to expose these members through a constrained existential. We can probably understand the expected behavior in some of the simpler cases on a case by case basis, but that approach doesn’t scale at all and is arbitrary. If they’re going to be supported an expert is going to need to be involved in the design.

Yes. I have some ideas regarding this topic.

One area you didn’t touch on is “opening” the existential? Is that out of scope for this proposal? That would be fine with me as this proposal is already taking on a lot. But if so, you should mention something about future directions as it is pretty closely related to this proposal.

Yes, existential opening is explicitly separate from this (although I wanted to mention it in the section where I talk about how Any<Equatable> is not very useful). But you are absolutely right, this proposal should discuss how it wants to interact with possible future directions.

Another area you didn’t touch on is whether Any constructs (and typealiases referring to them) should be usable as generic constraints. I would expect this to be possible but I think we need to spell it out.

I'm hoping for community input. This is a tricky subject, and at some point we'll bump into implementation limitations.

I don’t think it’s too tricky. You can just unpack the constraints of the Any into the list of generic constraints. Maybe I’m missing something, but I don’t think so.

-Matthew

Regards
(From mobile)

I've put together a considerably more detailed draft proposal, taking into account as much of Matthew's feedback as I could. You can find it below:

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

Since there is no chance this will come up for review anytime soon, I expect to make significant revisions to it over the next month or so. Any feedback would be greatly appreciated.

This is very much Swift 4 territory, but I can’t help myself… so…

The actual feature description is spread out through this very long document, with user-facing ideas (e.g., using “anonymous associated types”) intermixed with deeper technical details (existential type equivalence), so it’s very daunting to read.

My reading was further complicated by the fact that some of these details seemed incomplete or predicated on different mechanisms than what I have gathered of the current compiler's inners, leaving me to wonder in what manner they constitute a proposal for changing them.

Please bring the user-facing features to the front (“Proposed Solution”) with examples, and save the deeper technical details for “Detailed Design”. You want more readers to make it through the part that affects them.

Shortcut 'dot' notation: If there is only one protocol with associated types specified in the requirements, and there are no nested Any<...> requirements with where clauses of their own, that protocol's name can be omitted from the whereclause constraints:

// Okay
// Would otherwise be Any< ~ where Collection.Element == Int>
let a : Any<class, Collection, Any<Streamable, CustomStringConvertible> where .Element == Int>

// NOT ALLOWED
// Both Collection and OptionSetType have associated types.
let b : Any<Collection, OptionSetType where .Element == Int>
FWIW, I think “.Element == Int” should be the only syntax. In generic signatures, if you have two different protocols with same-named associated types, and a given type parameter (or associated type) conforms to both protocols, the associated types are (implicitly) made equivalent via an inferred same-type constraint. So there’s no reason to introduce the “Collection.Element == Int” syntax, because the “Collection” part is basically irrelevant.

Once existentials have been suitably enhanced, there is a strong analogy between an existential and a generic signature with a single type parameter that you can’t name. An existential Any<Collection where .Element : Equatable> has most of the same characteristics as a generic something with the signature <T : Collection where T.Element : Equatable>. Specifically, the sections on “Existential type equivalence”, “Ordering”, “Real types to anonymous associated types”, “Anonymous associated types to real types”. could be reduced to a few small, simple examples and a mention of the analogous behavior of generics.

The "member exposure" section would also have made it to this list (statements like 'Their metatypes are also available, although subject to the same restrictions as the types themselves' is an example of what felt incomplete in the context it was presented in).

It will be far easier to explain this way, and readers don’t need to get immersed in the details. Where there are differences vs. generics, that’s important to point out.

“Associated typealias rewriting”: this also falls out of the equivalence with generics + SE-0092.

“Associated types and member exposure”: you don’t make the point that it only makes sense to refer to the associated types of a let constant; a var could change its type dynamically, which would invalidate the typing rules. Did you consider just using “x.dynamicType” in the type grammar for this? It’s more general, in that you can refer to associated types but also talk about the dynamic type of “x” itself, e.g.,

  let x: Equatable = …
  let y: Equatable = …
  if let yAsX = y as? x.dynamicType { … x == yAsX … }

which is (almost?) as powerful as a general “open” expression.

I’m not a fan of the “anonymous associated types” terminology: these are associated types of a type of some runtime-defined value. The only thing “anonymous” about them is that it’s harder to spell the base type; otherwise, they’re just like associated types of a generic type parameter. Again, the generics analogy is strong here.

Isn't an 'anonymous associated type' as presented synonymous for 'an existential type used in the position of associated type to another existential type'? The section on 'anonymous associated types to real type' would become partially redundant with the general notion of existentials, baring a few possible extra usage limitations.

FWIW, I don’t think we’ll ever need “opening existentials” with what you’ve described here. Also, remember that a method of a protocol extension essentially opens “Self”, so we already have one way to open an existential (and that’s probably enough).

I was a little surprised you didn’t point out that AnyObject could become

  typealias AnyObject = Any<class>

or give the nice “AnyCollection” syntax:

  typealias AnyCollection<T> = Any<Collection where .Element == T>

the latter of which is fairly important, because it gives nice syntactic sure to one of the most highly-requested features [*]. I’d suggest having that example very, very early.

  - Doug

[*] That generally comes in as “Swift should have parameterized protocols…”

I am unclear about the Metatype section. I was under the impressions that all reflective knownledge of a given type was esposed via Mirrors.

···

On Jun 6, 2016, at 1:20 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On May 18, 2016, at 12:35 AM, Austin Zheng <austinzheng@gmail.com> wrote:

Austin

On Tue, May 17, 2016 at 9:52 PM, Austin Zheng <austinzheng@gmail.com> wrote:

On Tue, May 17, 2016 at 1:25 PM, Matthew Johnson <matthew@anandabits.com> wrote:

Within the angle brackets are zero or more 'clauses'. Clauses are separated by semicolons. (This is so commas can be used in where constraints, below. Better ideas are welcome. Maybe it's not necessary; we can use commas exclusively.)

I’m not a fan of the semicolon idea. I don’t see any reason for this. The `where` keyword separates the protocol list from the constraints just fine. The list on either side should be able to use commas with no problem (or line breaks if that proposal goes through).

I'm leaning towards getting rid of the commas, but would like to write out a few 'dummy' examples to see if there are any readability issues that arise.

Replaced with what? Whitespace separation? I suppose that might work for the protocol list but it feels inconsistent with the rest of Swift. Commas plus (hopefully) the alternative of newline seem like the right direction to me.

Sorry, I completely misspoke (mistyped?). I meant I want to get rid of the semicolons and use commas. I've come to the conclusion that there are no readability issues, protocol<> already uses commas, and semicolons used in this manner don't have a precedent anywhere else in the language.

There are five different possible clauses:

'class'. Must be the first clause, if present. Places a constraint on the existential to be any class type. (Implies: Only one can exist. Mutually exclusive with class name clause.)

(In the future a follow-up proposal should add in 'struct' or 'value' as a counterpart.)

If we’re going to allow `struct` we should also allow `enum`. `value` would allow either of those.

Of course. A future proposal can allow list members to discuss the exact details as to how struct, value, or enum specifiers should work.

Yep, agree. Just mentioning that if we’re going to reference it we should not leave obvious holes in what would be considered. :)

Absolutely.

Class name. Must be the first clause, if present. (Implies: Only one can exist. Mutually exclusive with 'class'.) Places a constraint on the existential (not really an existential anymore) to be an instance of the class, or one of its subclasses.

It is still be an existential if it includes protocol requirements that the class does not fulfill. For example, you might have Any<UIView, SomeProtocol> where UIView does not conform to SomeProtocol, but various subclasses do.

Fair enough. (I don't think the way things work would be affected.)

Your proposal doesn’t discuss composing Any in the way that Adrian’s did like this:

typealias Foo = Any<SomeClass, SomeProtocol, OtherProtocol>
Any<AnotherProtocol, Foo>

I didn't think it needed to be discussed. An Any<...> existential type is a type 'expression' just like any other, and should be allowed to participate in other Any<...>s.

I like the idea of composition as it allows us to factor out constraints. If we are going to do that we should allow a class to be specified in the composition as long is it is a subclass of all class requirements of Any types it composes. For example, this should be allowed:

typealias Bar = Any<SubclassOfSomeClass, Foo, AnotherProtocol>

This is still one class requirement for Bar, it just refines the class requirement of Foo to be SubclassOfSomeClass rather than just SomeClass.

This is a good point. There should be clarification as to how special cases of Any<...> used in another Any<...> behave. For example, like you said Any<MyClass, Any<SomeSubclassOfMyClass, Protocol>> should be valid. This will go into any proposal that emerges from the discussion.

Yes, this is why we need to discuss Any composition. There are also cases of incompatible associated type constraints which need to be rejected (such as composing two Any’s where one has Element == String and another has Element == Int).

Example: Any<UIViewController; UITableViewDataSource; UITableViewDelegate>
"Any UIViewController or subclass which also satisfies the table view data source and delegate protocols"
Dynamic protocol. This is entirely composed of the name of a protocol which has no associated types or Self requirement.
Example: Any<CustomStringConvertible; BooleanType>
"Any type which conforms to both the CustomStringConvertible and BooleanType protocols"

I'm going to use 'static protocol' to refer to a protocol with associated types or self requirements. Feel free to propose a more sound name.

Self-contained static protocol, simple. This is composed of the name of a static protocol, optionally followed by a 'where' clause in which the associated types can be constrained (with any of the three basic conformance types: subclassing, protocol conformance, or type equality). Associated types are referred to with a leading dot.

Please do not introduce terms “dynamic protocol” and “static protocol”. We want to support existentials of protocols that have self or associated type requirements. The dynamic vs static distinction is a limitation of the current implementation of Swift and doesn’t make sense for the long term vision.

I'm not trying to introduce new terms, these are just placeholders. At the same time "protocols with self or associated type requirements" is cumbersome to work with and it would be nice for someone to come up with a descriptive term of art for referring to them.

I agree that a better term would be useful. In the meantime, I would prefer something like “trivial” and “nontrivial” protocols.

I've decided to just use the full name until the community comes up with better names. Clarity is preferable to brevity in this case.

Example: Any<Collection where .Generator.Element : NSObject, .Generator.Element : SomeProtocol>
"Any type that is a Collection, whose elements are NSObjects or their subclasses conforming to SomeProtocol.”

Swift does not allow disjunction of requirements. Only conjunctions are supported. That means the correct reading is:

"Any type that is a Collection, whose elements are NSObjects and their subclasses conforming to SomeProtocol.”

Yes, that is what I meant. "whose elements are (NSObjects or their subclasses) conforming to SomeProtocol”.

Ok, good. Wasn’t quite clear to me.

Yes, the verbiage will need to be clearer in the future. That sentence could be ambiguously parsed.

Bound static protocol. This is the same as a self-contained static protocol, but with a leading "<name> as " which binds the protocol to a generic typealias. The name can be then be used in subsequent clauses to build constraints.

Example: Any<T as Collection; IntegerLiteralConvertible where .IntegerLiteralType == T.Element>.
"Any type that is a Collection, and also can be built from an integer literal, in which the collection elements are the same type as the type of the integer used for the integer literal conformance.”

I’m not sure about this, but if we’re going to do it it should be the other way around: `Collection as T` with the alias after the name of the protocol.

I like this, it flows better. "Protocol as T where Protocol.Foo == Int, Protocol.Bar : Baz”.

Why did you introduce an alias here and then not use it? Did you mean "Protocol as T where T.Foo == Int, T.Bar : Baz"

Another result of rushing to compose an email. Sorry!

You are also using “dot shorthand” here to refer to an associated type of IntegerLiteralConvertible. I think “dot shorthand” should be limited to cases where there is only one protocol that is getting constrained. In other cases, we need to be clear about which protocol we are referring to.

I borrowed dot shorthand from the generics manifesto. But you are right, it should only be allowed if there is one protocol with associated types or self requirements clause in the Any<...> construction.

I would actually go further and limit it to one protocol period, and possibly even to one protocol and no type names (as types can have nested types and typealiases). When we allow shorthand it should be immediately unambiguous what the shorthand references with no need to look at type or protocol declarations.

It might be desirable to propose the proposal with no allowance for shorthand, and have the dot shorthand be a smaller follow-up proposal.

There will be rules to prevent recursive nesting. For example, if generic typealiases are allowed, they cannot refer to each other in a circular manner (like how structs can't contain themeselves, and you can't create a cyclic graph of enums containing themselves).

How an existential can be used depends on what guarantees are provided by the clauses. For example, 'Any<Equatable>' can't be used for much; if there were any methods on Equatable that did not use the associated types at all you'd be able to call them, but that's about it. However, 'Any<Equatable where .Self == String>' would allow for == to be called on instances. (This is a stupid example, since Any<Equatable where .Self == String> is equivalent to 'String', but there are almost certainly useful examples one could come up with.)

In order of increasing 'power':
Don't constrain any associated types. You can pass around Any<Equatable>s, but that's about it.
Constrain associated types to conform to protocols.
Fully constrain associated types.

I think we need to spell out pretty clearly what members we expect to be available or not available. This section probably needs the most design and elaboration.

For example, we probably can’t access a member who uses an associated type as an input unless it is constrained to a specific type. On the other hand output types probably don’t need to limit access to a member. However, if the output type is Self or an associated type the visible signature would have an output type which has the relevant constraints of the existential applied, but no more. In some cases this means the output type would simply be Any.

Absolutely. This is vaguely what I had in mind but I wanted to get something down first. Thanks for thinking through some of the implications :).

That’s what I thought. Just wanted to start the process of elaborating expectations.

Where this really gets tricky is for compound types like functions, generic types, etc. Working out the details in these cases is pretty complex. I will defer to Doug on whether it is best to just defer those cases to the future, leave them up to the implementer, or try to work out all of the relevant details in the proposal (in which case we probably need a type system expert to help!).

Yes, exactly! For example, can Any<...> existentials involving protocols with associated types or self requirements be used within generic function or type definitions? Maybe there's an argument that existential types of this nature are redundant if you have access to generics (e.g. defining a property on a generic type that is a Collection containing Ints; you should be able to do that today). On the other hand, maybe there are use cases I haven't thought of…

I see no reason they shouldn’t be. They are not redundant at all. For example, you may want to store instances in a heterogeneous collection. You need existentials to do that.

A simple example of what I was referring to there is something like this:

protocol P {
    associatedtype Foo

    func bar(callback: (Foo) -> ())
}

In other words, types in the signature of a protocol member are complex types that reference Self or associated types. I think you really need a formal understanding of the type system to understand how to expose these members through a constrained existential. We can probably understand the expected behavior in some of the simpler cases on a case by case basis, but that approach doesn’t scale at all and is arbitrary. If they’re going to be supported an expert is going to need to be involved in the design.

Yes. I have some ideas regarding this topic.

One area you didn’t touch on is “opening” the existential? Is that out of scope for this proposal? That would be fine with me as this proposal is already taking on a lot. But if so, you should mention something about future directions as it is pretty closely related to this proposal.

Yes, existential opening is explicitly separate from this (although I wanted to mention it in the section where I talk about how Any<Equatable> is not very useful). But you are absolutely right, this proposal should discuss how it wants to interact with possible future directions.

Another area you didn’t touch on is whether Any constructs (and typealiases referring to them) should be usable as generic constraints. I would expect this to be possible but I think we need to spell it out.

I'm hoping for community input. This is a tricky subject, and at some point we'll bump into implementation limitations.

I don’t think it’s too tricky. You can just unpack the constraints of the Any into the list of generic constraints. Maybe I’m missing something, but I don’t think so.

-Matthew

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

an alternative to the Any<> syntax with tentative grammar and some examples (still a WIP)

···

On Jun 6, 2016, at 1:20 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On May 18, 2016, at 12:35 AM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

I've put together a considerably more detailed draft proposal, taking into account as much of Matthew's feedback as I could. You can find it below:

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

Since there is no chance this will come up for review anytime soon, I expect to make significant revisions to it over the next month or so. Any feedback would be greatly appreciated.

This is very much Swift 4 territory, but I can’t help myself… so…

The actual feature description is spread out through this very long document, with user-facing ideas (e.g., using “anonymous associated types”) intermixed with deeper technical details (existential type equivalence), so it’s very daunting to read. Please bring the user-facing features to the front (“Proposed Solution”) with examples, and save the deeper technical details for “Detailed Design”. You want more readers to make it through the part that affects them.

Shortcut 'dot' notation: If there is only one protocol with associated types specified in the requirements, and there are no nested Any<...> requirements with where clauses of their own, that protocol's name can be omitted from the whereclause constraints:

// Okay
// Would otherwise be Any< ~ where Collection.Element == Int>
let a : Any<class, Collection, Any<Streamable, CustomStringConvertible> where .Element == Int>

// NOT ALLOWED
// Both Collection and OptionSetType have associated types.
let b : Any<Collection, OptionSetType where .Element == Int>
FWIW, I think “.Element == Int” should be the only syntax. In generic signatures, if you have two different protocols with same-named associated types, and a given type parameter (or associated type) conforms to both protocols, the associated types are (implicitly) made equivalent via an inferred same-type constraint. So there’s no reason to introduce the “Collection.Element == Int” syntax, because the “Collection” part is basically irrelevant.

Once existentials have been suitably enhanced, there is a strong analogy between an existential and a generic signature with a single type parameter that you can’t name. An existential Any<Collection where .Element : Equatable> has most of the same characteristics as a generic something with the signature <T : Collection where T.Element : Equatable>. Specifically, the sections on “Existential type equivalence”, “Ordering”, “Real types to anonymous associated types”, “Anonymous associated types to real types”. could be reduced to a few small, simple examples and a mention of the analogous behavior of generics. It will be far easier to explain this way, and readers don’t need to get immersed in the details. Where there are differences vs. generics, that’s important to point out.

“Associated typealias rewriting”: this also falls out of the equivalence with generics + SE-0092.

“Associated types and member exposure”: you don’t make the point that it only makes sense to refer to the associated types of a let constant; a var could change its type dynamically, which would invalidate the typing rules. Did you consider just using “x.dynamicType” in the type grammar for this? It’s more general, in that you can refer to associated types but also talk about the dynamic type of “x” itself, e.g.,

  let x: Equatable = …
  let y: Equatable = …
  if let yAsX = y as? x.dynamicType { … x == yAsX … }

which is (almost?) as powerful as a general “open” expression.

I’m not a fan of the “anonymous associated types” terminology: these are associated types of a type of some runtime-defined value. The only thing “anonymous” about them is that it’s harder to spell the base type; otherwise, they’re just like associated types of a generic type parameter. Again, the generics analogy is strong here.

FWIW, I don’t think we’ll ever need “opening existentials” with what you’ve described here. Also, remember that a method of a protocol extension essentially opens “Self”, so we already have one way to open an existential (and that’s probably enough).

I was a little surprised you didn’t point out that AnyObject could become

  typealias AnyObject = Any<class>

or give the nice “AnyCollection” syntax:

  typealias AnyCollection<T> = Any<Collection where .Element == T>

the latter of which is fairly important, because it gives nice syntactic sure to one of the most highly-requested features [*]. I’d suggest having that example very, very early.

  - Doug

[*] That generally comes in as “Swift should have parameterized protocols…”

Austin

On Tue, May 17, 2016 at 9:52 PM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

On Tue, May 17, 2016 at 1:25 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

Within the angle brackets are zero or more 'clauses'. Clauses are separated by semicolons. (This is so commas can be used in where constraints, below. Better ideas are welcome. Maybe it's not necessary; we can use commas exclusively.)

I’m not a fan of the semicolon idea. I don’t see any reason for this. The `where` keyword separates the protocol list from the constraints just fine. The list on either side should be able to use commas with no problem (or line breaks if that proposal goes through).

I'm leaning towards getting rid of the commas, but would like to write out a few 'dummy' examples to see if there are any readability issues that arise.

Replaced with what? Whitespace separation? I suppose that might work for the protocol list but it feels inconsistent with the rest of Swift. Commas plus (hopefully) the alternative of newline seem like the right direction to me.

Sorry, I completely misspoke (mistyped?). I meant I want to get rid of the semicolons and use commas. I've come to the conclusion that there are no readability issues, protocol<> already uses commas, and semicolons used in this manner don't have a precedent anywhere else in the language.

There are five different possible clauses:

'class'. Must be the first clause, if present. Places a constraint on the existential to be any class type. (Implies: Only one can exist. Mutually exclusive with class name clause.)

(In the future a follow-up proposal should add in 'struct' or 'value' as a counterpart.)

If we’re going to allow `struct` we should also allow `enum`. `value` would allow either of those.

Of course. A future proposal can allow list members to discuss the exact details as to how struct, value, or enum specifiers should work.

Yep, agree. Just mentioning that if we’re going to reference it we should not leave obvious holes in what would be considered. :)

Absolutely.

Class name. Must be the first clause, if present. (Implies: Only one can exist. Mutually exclusive with 'class'.) Places a constraint on the existential (not really an existential anymore) to be an instance of the class, or one of its subclasses.

It is still be an existential if it includes protocol requirements that the class does not fulfill. For example, you might have Any<UIView, SomeProtocol> where UIView does not conform to SomeProtocol, but various subclasses do.

Fair enough. (I don't think the way things work would be affected.)

Your proposal doesn’t discuss composing Any in the way that Adrian’s did like this:

typealias Foo = Any<SomeClass, SomeProtocol, OtherProtocol>
Any<AnotherProtocol, Foo>

I didn't think it needed to be discussed. An Any<...> existential type is a type 'expression' just like any other, and should be allowed to participate in other Any<...>s.

I like the idea of composition as it allows us to factor out constraints. If we are going to do that we should allow a class to be specified in the composition as long is it is a subclass of all class requirements of Any types it composes. For example, this should be allowed:

typealias Bar = Any<SubclassOfSomeClass, Foo, AnotherProtocol>

This is still one class requirement for Bar, it just refines the class requirement of Foo to be SubclassOfSomeClass rather than just SomeClass.

This is a good point. There should be clarification as to how special cases of Any<...> used in another Any<...> behave. For example, like you said Any<MyClass, Any<SomeSubclassOfMyClass, Protocol>> should be valid. This will go into any proposal that emerges from the discussion.

Yes, this is why we need to discuss Any composition. There are also cases of incompatible associated type constraints which need to be rejected (such as composing two Any’s where one has Element == String and another has Element == Int).

Example: Any<UIViewController; UITableViewDataSource; UITableViewDelegate>
"Any UIViewController or subclass which also satisfies the table view data source and delegate protocols"
Dynamic protocol. This is entirely composed of the name of a protocol which has no associated types or Self requirement.
Example: Any<CustomStringConvertible; BooleanType>
"Any type which conforms to both the CustomStringConvertible and BooleanType protocols"

I'm going to use 'static protocol' to refer to a protocol with associated types or self requirements. Feel free to propose a more sound name.

Self-contained static protocol, simple. This is composed of the name of a static protocol, optionally followed by a 'where' clause in which the associated types can be constrained (with any of the three basic conformance types: subclassing, protocol conformance, or type equality). Associated types are referred to with a leading dot.

Please do not introduce terms “dynamic protocol” and “static protocol”. We want to support existentials of protocols that have self or associated type requirements. The dynamic vs static distinction is a limitation of the current implementation of Swift and doesn’t make sense for the long term vision.

I'm not trying to introduce new terms, these are just placeholders. At the same time "protocols with self or associated type requirements" is cumbersome to work with and it would be nice for someone to come up with a descriptive term of art for referring to them.

I agree that a better term would be useful. In the meantime, I would prefer something like “trivial” and “nontrivial” protocols.

I've decided to just use the full name until the community comes up with better names. Clarity is preferable to brevity in this case.

Example: Any<Collection where .Generator.Element : NSObject, .Generator.Element : SomeProtocol>
"Any type that is a Collection, whose elements are NSObjects or their subclasses conforming to SomeProtocol.”

Swift does not allow disjunction of requirements. Only conjunctions are supported. That means the correct reading is:

"Any type that is a Collection, whose elements are NSObjects and their subclasses conforming to SomeProtocol.”

Yes, that is what I meant. "whose elements are (NSObjects or their subclasses) conforming to SomeProtocol”.

Ok, good. Wasn’t quite clear to me.

Yes, the verbiage will need to be clearer in the future. That sentence could be ambiguously parsed.

Bound static protocol. This is the same as a self-contained static protocol, but with a leading "<name> as " which binds the protocol to a generic typealias. The name can be then be used in subsequent clauses to build constraints.

Example: Any<T as Collection; IntegerLiteralConvertible where .IntegerLiteralType == T.Element>.
"Any type that is a Collection, and also can be built from an integer literal, in which the collection elements are the same type as the type of the integer used for the integer literal conformance.”

I’m not sure about this, but if we’re going to do it it should be the other way around: `Collection as T` with the alias after the name of the protocol.

I like this, it flows better. "Protocol as T where Protocol.Foo == Int, Protocol.Bar : Baz”.

Why did you introduce an alias here and then not use it? Did you mean "Protocol as T where T.Foo == Int, T.Bar : Baz"

Another result of rushing to compose an email. Sorry!

You are also using “dot shorthand” here to refer to an associated type of IntegerLiteralConvertible. I think “dot shorthand” should be limited to cases where there is only one protocol that is getting constrained. In other cases, we need to be clear about which protocol we are referring to.

I borrowed dot shorthand from the generics manifesto. But you are right, it should only be allowed if there is one protocol with associated types or self requirements clause in the Any<...> construction.

I would actually go further and limit it to one protocol period, and possibly even to one protocol and no type names (as types can have nested types and typealiases). When we allow shorthand it should be immediately unambiguous what the shorthand references with no need to look at type or protocol declarations.

It might be desirable to propose the proposal with no allowance for shorthand, and have the dot shorthand be a smaller follow-up proposal.

There will be rules to prevent recursive nesting. For example, if generic typealiases are allowed, they cannot refer to each other in a circular manner (like how structs can't contain themeselves, and you can't create a cyclic graph of enums containing themselves).

How an existential can be used depends on what guarantees are provided by the clauses. For example, 'Any<Equatable>' can't be used for much; if there were any methods on Equatable that did not use the associated types at all you'd be able to call them, but that's about it. However, 'Any<Equatable where .Self == String>' would allow for == to be called on instances. (This is a stupid example, since Any<Equatable where .Self == String> is equivalent to 'String', but there are almost certainly useful examples one could come up with.)

In order of increasing 'power':
Don't constrain any associated types. You can pass around Any<Equatable>s, but that's about it.
Constrain associated types to conform to protocols.
Fully constrain associated types.

I think we need to spell out pretty clearly what members we expect to be available or not available. This section probably needs the most design and elaboration.

For example, we probably can’t access a member who uses an associated type as an input unless it is constrained to a specific type. On the other hand output types probably don’t need to limit access to a member. However, if the output type is Self or an associated type the visible signature would have an output type which has the relevant constraints of the existential applied, but no more. In some cases this means the output type would simply be Any.

Absolutely. This is vaguely what I had in mind but I wanted to get something down first. Thanks for thinking through some of the implications :).

That’s what I thought. Just wanted to start the process of elaborating expectations.

Where this really gets tricky is for compound types like functions, generic types, etc. Working out the details in these cases is pretty complex. I will defer to Doug on whether it is best to just defer those cases to the future, leave them up to the implementer, or try to work out all of the relevant details in the proposal (in which case we probably need a type system expert to help!).

Yes, exactly! For example, can Any<...> existentials involving protocols with associated types or self requirements be used within generic function or type definitions? Maybe there's an argument that existential types of this nature are redundant if you have access to generics (e.g. defining a property on a generic type that is a Collection containing Ints; you should be able to do that today). On the other hand, maybe there are use cases I haven't thought of…

I see no reason they shouldn’t be. They are not redundant at all. For example, you may want to store instances in a heterogeneous collection. You need existentials to do that.

A simple example of what I was referring to there is something like this:

protocol P {
    associatedtype Foo

    func bar(callback: (Foo) -> ())
}

In other words, types in the signature of a protocol member are complex types that reference Self or associated types. I think you really need a formal understanding of the type system to understand how to expose these members through a constrained existential. We can probably understand the expected behavior in some of the simpler cases on a case by case basis, but that approach doesn’t scale at all and is arbitrary. If they’re going to be supported an expert is going to need to be involved in the design.

Yes. I have some ideas regarding this topic.

One area you didn’t touch on is “opening” the existential? Is that out of scope for this proposal? That would be fine with me as this proposal is already taking on a lot. But if so, you should mention something about future directions as it is pretty closely related to this proposal.

Yes, existential opening is explicitly separate from this (although I wanted to mention it in the section where I talk about how Any<Equatable> is not very useful). But you are absolutely right, this proposal should discuss how it wants to interact with possible future directions.

Another area you didn’t touch on is whether Any constructs (and typealiases referring to them) should be usable as generic constraints. I would expect this to be possible but I think we need to spell it out.

I'm hoping for community input. This is a tricky subject, and at some point we'll bump into implementation limitations.

I don’t think it’s too tricky. You can just unpack the constraints of the Any into the list of generic constraints. Maybe I’m missing something, but I don’t think so.

-Matthew

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

added a few comments to extended_existentials.md · GitHub to clarify the rational. it is by no mean the only way or a good way, but IMO there are far worse ways to do the same.

···

On Jun 6, 2016, at 1:20 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

On May 18, 2016, at 12:35 AM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

I've put together a considerably more detailed draft proposal, taking into account as much of Matthew's feedback as I could. You can find it below:

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

Since there is no chance this will come up for review anytime soon, I expect to make significant revisions to it over the next month or so. Any feedback would be greatly appreciated.

This is very much Swift 4 territory, but I can’t help myself… so…

The actual feature description is spread out through this very long document, with user-facing ideas (e.g., using “anonymous associated types”) intermixed with deeper technical details (existential type equivalence), so it’s very daunting to read. Please bring the user-facing features to the front (“Proposed Solution”) with examples, and save the deeper technical details for “Detailed Design”. You want more readers to make it through the part that affects them.

Shortcut 'dot' notation: If there is only one protocol with associated types specified in the requirements, and there are no nested Any<...> requirements with where clauses of their own, that protocol's name can be omitted from the whereclause constraints:

// Okay
// Would otherwise be Any< ~ where Collection.Element == Int>
let a : Any<class, Collection, Any<Streamable, CustomStringConvertible> where .Element == Int>

// NOT ALLOWED
// Both Collection and OptionSetType have associated types.
let b : Any<Collection, OptionSetType where .Element == Int>
FWIW, I think “.Element == Int” should be the only syntax. In generic signatures, if you have two different protocols with same-named associated types, and a given type parameter (or associated type) conforms to both protocols, the associated types are (implicitly) made equivalent via an inferred same-type constraint. So there’s no reason to introduce the “Collection.Element == Int” syntax, because the “Collection” part is basically irrelevant.

Once existentials have been suitably enhanced, there is a strong analogy between an existential and a generic signature with a single type parameter that you can’t name. An existential Any<Collection where .Element : Equatable> has most of the same characteristics as a generic something with the signature <T : Collection where T.Element : Equatable>. Specifically, the sections on “Existential type equivalence”, “Ordering”, “Real types to anonymous associated types”, “Anonymous associated types to real types”. could be reduced to a few small, simple examples and a mention of the analogous behavior of generics. It will be far easier to explain this way, and readers don’t need to get immersed in the details. Where there are differences vs. generics, that’s important to point out.

“Associated typealias rewriting”: this also falls out of the equivalence with generics + SE-0092.

“Associated types and member exposure”: you don’t make the point that it only makes sense to refer to the associated types of a let constant; a var could change its type dynamically, which would invalidate the typing rules. Did you consider just using “x.dynamicType” in the type grammar for this? It’s more general, in that you can refer to associated types but also talk about the dynamic type of “x” itself, e.g.,

  let x: Equatable = …
  let y: Equatable = …
  if let yAsX = y as? x.dynamicType { … x == yAsX … }

which is (almost?) as powerful as a general “open” expression.

I’m not a fan of the “anonymous associated types” terminology: these are associated types of a type of some runtime-defined value. The only thing “anonymous” about them is that it’s harder to spell the base type; otherwise, they’re just like associated types of a generic type parameter. Again, the generics analogy is strong here.

FWIW, I don’t think we’ll ever need “opening existentials” with what you’ve described here. Also, remember that a method of a protocol extension essentially opens “Self”, so we already have one way to open an existential (and that’s probably enough).

I was a little surprised you didn’t point out that AnyObject could become

  typealias AnyObject = Any<class>

or give the nice “AnyCollection” syntax:

  typealias AnyCollection<T> = Any<Collection where .Element == T>

the latter of which is fairly important, because it gives nice syntactic sure to one of the most highly-requested features [*]. I’d suggest having that example very, very early.

  - Doug

[*] That generally comes in as “Swift should have parameterized protocols…”

@doug : watched and very much appreciated your presentation on what makes a good API… reminded me of a job interview with Bertand Serlet a few years ago.. you guys have managed to remain very consistent over a very long period of time, and it looks like Swift is following in the foot-steps.

You should definitely submit your proposal first. I'll review your nesting
rules later. f your proposal is accepted I will edit mine to match.

Allowing value types belong in a separate proposal; I think this is
something people would want to discuss on their own merits. For example,
the `class` keyword already exists, and can be used in several places to
force something to be a class. People would want similar value type
keywords to also work everywhere `class` does, not only in a composite type
declaration.

Austin

···

On Wed, May 18, 2016 at 6:49 AM, Adrian Zubarev via swift-evolution < swift-evolution@swift.org> wrote:

I added a note to my proposal which makes it clear that the `Any<>` I
proposed represents the simple/base form that Swift 3 should integrate, if
accepted. Later `Any<>` could be enhanced without any breaking changes.

I’m not sure if your and my nesting rules do fit together, we might
reconsider mine before the pull request is accepted.

- Also I don’t see the point why `Any<Any<ProtocolA, ProtocolB>>` this is
illegal!?

`Any<>` proposed by me will allow that, even it its useless form the point
of the readers view. This type is inferred to `Any<ProtocolA, ProtocolB>`.

Why do you not allow value types in your proposal?

--
Adrian Zubarev
Sent with Airmail

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

You should definitely submit your proposal first. I'll review your nesting rules later. f your proposal is accepted I will edit mine to match.

Allowing value types belong in a separate proposal; I think this is something people would want to discuss on their own merits. For example, the `class` keyword already exists, and can be used in several places to force something to be a class. People would want similar value type keywords to also work everywhere `class` does, not only in a composite type declaration.

I agree about value types. I'm not sure what benefit that provides. What we really need here is the ability to constrain value semantics. Simply constraining to a value type does not do that (a struct can have a class property and use it in a way that violates value semantics). Dave A and I have had a pretty lengthy discussion of desirable se,antic guarantees recently.

···

Sent from my iPad

On May 18, 2016, at 10:25 AM, Austin Zheng via swift-evolution <swift-evolution@swift.org> wrote:

Austin

On Wed, May 18, 2016 at 6:49 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:
I added a note to my proposal which makes it clear that the `Any<>` I proposed represents the simple/base form that Swift 3 should integrate, if accepted. Later `Any<>` could be enhanced without any breaking changes.

I’m not sure if your and my nesting rules do fit together, we might reconsider mine before the pull request is accepted.

- Also I don’t see the point why `Any<Any<ProtocolA, ProtocolB>>` this is illegal!?

  `Any<>` proposed by me will allow that, even it its useless form the point of the readers view. This type is inferred to `Any<ProtocolA, ProtocolB>`.

Why do you not allow value types in your proposal?

--
Adrian Zubarev
Sent with Airmail

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

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

I strongly prefer keeping the class requirement first, both for readability
and because this reflects how protocol and 'class' or superclass
conformances already work when defining classes or protocols. I think "look
at the first argument, and then recursively look at the first arguments of
any nested Any's for class requirements" is straightforward enough not to
confuse people, and helps keep the type definitions organized.

Austin

···

On Wed, May 18, 2016 at 12:11 PM, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

On May 18, 2016, at 12:12 PM, Adrian Zubarev via swift-evolution < > swift-evolution@swift.org> wrote:

Okay now I feel like we’re merging everything we came up until now :D I’d
love to see something like this happen to Swift, because `Any` seems to be
a really powerful beast one day.

One quick question: Do we really need this "This must be the first
requirement, if present.“?

I’m on the fence about this. The reason it would matter is for
readability. The counter argument to that is that you can’t rely on the
first argument being a class to determine whether a composed Any will have
a class constraint or not.

I’d say the compiler should reorder all types as it wants to. Current
protocol<> already doing this today.

e.g.

protocol A {}
protocol B {}

typealias C = protocol<A, B>
typealias D = protocol<B, A>

print(C) // prints protocol<A, B>
print(D) // prints protocol<A, B>
print(C.self == D.self) // prints true

Basically what I mean

Any<SomeProtocol, class, AnotherProtocol>

Any<Any<ProtocolA, ProtocolB>, UIView>

should be valid.

--
Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 18:30:02, Austin Zheng via swift-evolution (
swift-evolution@swift.org) schrieb:

I made heavy revisions to my proposal to reflect all the great feedback I
got from you and several other folks (
https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md
).

Here are a couple of thoughts:

- I used 'requirements' because that's how the grammar describes similar
constructs elsewhere in Swift. Open to change, though.

- Having only one where clause makes complete sense. Protocol extensions
and generics all use one where clause, so should this construct.

- The "P can be used in lieu of Any<P>" just means you can declare e.g. "x
: Equatable" if you want, instead of "x : Any<Equatable>", just like you
can with protocols without associated types or self requirements today.

- I've come to the conclusion that it's probably best to propose the
proposal in the most general form, and allow any reviewers on the core team
to excise parts they don't think are useful enough or are too difficult to
implement. In that spirit, the 'simple Any<...>' construct is gone and Any
usage is fully general.

- I moved discussion of typealiases back into the main protocol, like you
said, because typealiases using existentials are *not* actually generic and
can be done today.

- I added some stuff about 'narrowing' existentials at point of use using
as?. This isn't part of 'opening existentials' and should fit in this
proposal nicely.

- I want a type expert to look at the 'usage' section, but I'm reasonably
sure there's not much more flexibility we can give the user. Parameter
associated types can't be treated as covariant (they are almost always
invariant or contravariant IIRC) and therefore they should only be
accessible if fully bound. Return types can be treated as covariant; some
languages do and some don't. (Swift falls into the second bucket.) I would
love to be wrong, though.

Austin

On May 18, 2016, at 8:45 AM, Matthew Johnson <matthew@anandabits.com> > wrote:

Sent from my iPad

On May 18, 2016, at 2:35 AM, Austin Zheng <austinzheng@gmail.com> wrote:

I've put together a considerably more detailed draft proposal, taking into
account as much of Matthew's feedback as I could. You can find it below:

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

Since there is no chance this will come up for review anytime soon, I
expect to make significant revisions to it over the next month or so. Any
feedback would be greatly appreciated.

Thank you for working on this! Great progress.

Minor nit, but I think the proper word is constraint rather than
requirement here:

"Within the angle brackets < and > are zero or more *requirements*.
Requirements are separated by commas."

Another tweak:

"P can be used in lieu of Any<P>, where P is a protocol with or without
associated type or self requirements."

This proposal is introducing generalized existentials. P and Any<P>
should be interchangeable for any protocol regardless of requirements of
the protocol. Existentials of protocols with self or associated type
requirements that do not include constraints will just expose limited
direct functionality. It would still be possible to attempt cast them to
concrete types to recover more functionality. In the future (after a
follow on proposal) it will also be possible to open the existential.

Thorsten pointed out that there should only be one where clause for the
whole existential. This follows the structure of generic type and function
constraints. It may also be worth removing the 'as' alias from this
proposal. This could be introduced as a stand alone proposal where it
would apply to any context with generic constraints.

Another item:
// NOT ALLOWED let a : Any<Any<ProtocolA, ProtocolB>>

Why is this not allowed? It is pointless, but should be allowed and
considered identical to the flattened syntax.

On dynamic casting, I don't believe it should be restricted in the way you
have defined here. Casting *to* an existential doesn't have anything to do
with opening an existential. We should allow casting to any existential
type.

On a similar note, I completely disagree with the limitation you specify
for use of Any in generic constraints precisely because of your
counterargument. In the discussion about moving the where clause it has
been noted that sometime it is necessary to apply a lot of constraints to
get the necessary effect. A mechanism for factoring constraints is highly
desirable and will greatly improve the readability of generic code.
Typealiases bound to Any can provide such a mechanism. Let's not
artificially restrict the use of it.

The section regarding members of a partly constrained existential needs to
be more fleshed out. We can't simply punt it to a future proposal.
However, I do think it is a good idea to wait until the core team has time
to participate in the discussion.

The section about defining typealias also should not be left to the
future. It is possible to define typealias with protocol<> today and to
use that alias in a generic constraint. Removing that capability would be
a regression. In fact, it's utility will increase significantly with this
proposal.

In general, I don't think we need the distinction between simple and full
Any. The whole idea of this proposal IMO should be fully generalizing
existentials. If restrictions are necessary they should be due to
(hopefully temporary) implementation considerations.

Austin

On Tue, May 17, 2016 at 9:52 PM, Austin Zheng <austinzheng@gmail.com> > wrote:

On Tue, May 17, 2016 at 1:25 PM, Matthew Johnson <matthew@anandabits.com> >> wrote:

Within the angle brackets are zero or more 'clauses'. Clauses are
separated by semicolons. (This is so commas can be used in where
constraints, below. Better ideas are welcome. Maybe it's not necessary; we
can use commas exclusively.)

I’m not a fan of the semicolon idea. I don’t see any reason for this.
The `where` keyword separates the protocol list from the constraints just
fine. The list on either side should be able to use commas with no problem
(or line breaks if that proposal goes through).

I'm leaning towards getting rid of the commas, but would like to write
out a few 'dummy' examples to see if there are any readability issues that
arise.

Replaced with what? Whitespace separation? I suppose that might work
for the protocol list but it feels inconsistent with the rest of Swift.
Commas plus (hopefully) the alternative of newline seem like the right
direction to me.

Sorry, I completely misspoke (mistyped?). I meant I want to get rid of
the semicolons and use commas. I've come to the conclusion that there are
no readability issues, protocol<> already uses commas, and semicolons used
in this manner don't have a precedent anywhere else in the language.

There are five different possible clauses:

   - 'class'. Must be the first clause, if present. Places a
   constraint on the existential to be any class type. (Implies: Only one can
   exist. Mutually exclusive with class name clause.)

(In the future a follow-up proposal should add in 'struct' or 'value'
as a counterpart.)

If we’re going to allow `struct` we should also allow `enum`. `value`
would allow either of those.

Of course. A future proposal can allow list members to discuss the exact
details as to how struct, value, or enum specifiers should work.

Yep, agree. Just mentioning that if we’re going to reference it we
should not leave obvious holes in what would be considered. :)

Absolutely.

   - Class name. Must be the first clause, if present. (Implies: Only
   one can exist. Mutually exclusive with 'class'.) Places a constraint on the
   existential (not really an existential anymore) to be an instance of the
   class, or one of its subclasses.

It is still be an existential if it includes protocol requirements that
the class does not fulfill. For example, you might have Any<UIView,
> where UIView does not conform to SomeProtocol, but various
subclasses do.

Fair enough. (I don't think the way things work would be affected.)

Your proposal doesn’t discuss composing Any in the way that Adrian’s
did like this:

typealias Foo = Any<SomeClass, SomeProtocol, OtherProtocol>
Any<AnotherProtocol, Foo>

I didn't think it needed to be discussed. An Any<...> existential type
is a type 'expression' just like any other, and should be allowed to
participate in other Any<...>s.

I like the idea of composition as it allows us to factor out
constraints. If we are going to do that we should allow a class to be
specified in the composition as long is it is a subclass of all class
requirements of Any types it composes. For example, this should be allowed:

typealias Bar = Any<SubclassOfSomeClass, Foo, AnotherProtocol>

This is still one class requirement for Bar, it just refines the class
requirement of Foo to be SubclassOfSomeClass rather than just SomeClass.

This is a good point. There should be clarification as to how special
cases of Any<...> used in another Any<...> behave. For example, like you
said Any<MyClass, Any<SomeSubclassOfMyClass, Protocol>> should be valid.
This will go into any proposal that emerges from the discussion.

Yes, this is why we need to discuss Any composition. There are also
cases of incompatible associated type constraints which need to be rejected
(such as composing two Any’s where one has Element == String and another
has Element == Int).

Example: Any<UIViewController; UITableViewDataSource;
>
"Any UIViewController or subclass which also satisfies the table view
data source and delegate protocols"

   - Dynamic protocol. This is entirely composed of the name of a
   protocol which has no associated types or Self requirement.

Example: Any<CustomStringConvertible; BooleanType>
"Any type which conforms to both the CustomStringConvertible and
BooleanType protocols"

I'm going to use 'static protocol' to refer to a protocol with
associated types or self requirements. Feel free to propose a more sound
name.

   - Self-contained static protocol, simple. This is composed of the
   name of a static protocol, optionally followed by a 'where' clause in which
   the associated types can be constrained (with any of the three basic
   conformance types: subclassing, protocol conformance, or type equality).
   Associated types are referred to with a leading dot.

Please do not introduce terms “dynamic protocol” and “static
protocol”. We want to support existentials of protocols that have self or
associated type requirements. The dynamic vs static distinction is a
limitation of the current implementation of Swift and doesn’t make sense
for the long term vision.

I'm not trying to introduce new terms, these are just placeholders. At
the same time "protocols with self or associated type requirements" is
cumbersome to work with and it would be nice for someone to come up with a
descriptive term of art for referring to them.

I agree that a better term would be useful. In the meantime, I would
prefer something like “trivial” and “nontrivial” protocols.

I've decided to just use the full name until the community comes up with
better names. Clarity is preferable to brevity in this case.

Example: Any<Collection where .Generator.Element : NSObject,
.Generator.Element : SomeProtocol>
"Any type that is a Collection, whose elements are NSObjects or their
subclasses conforming to SomeProtocol.”

Swift does not allow disjunction of requirements. Only conjunctions
are supported. That means the correct reading is:

"Any type that is a Collection, whose elements are NSObjects *and* their
subclasses conforming to SomeProtocol.”

Yes, that is what I meant. "whose elements are (NSObjects or their
subclasses) conforming to SomeProtocol”.

Ok, good. Wasn’t quite clear to me.

Yes, the verbiage will need to be clearer in the future. That sentence
could be ambiguously parsed.

   - Bound static protocol. This is the same as a self-contained
   static protocol, but with a leading "<name> as " which binds the protocol
   to a generic typealias. The name can be then be used in subsequent clauses
   to build constraints.

Example: Any<T as Collection; IntegerLiteralConvertible where
.IntegerLiteralType == T.Element>.
"Any type that is a Collection, and also can be built from an integer
literal, in which the collection elements are the same type as the type of
the integer used for the integer literal conformance.”

I’m not sure about this, but if we’re going to do it it should be the
other way around: `Collection as T` with the alias *after* the name of
the protocol.

I like this, it flows better. "Protocol as T where Protocol.Foo == Int,
Protocol.Bar : Baz”.

Why did you introduce an alias here and then not use it? Did you mean
"Protocol as T where T.Foo == Int, T.Bar : Baz"

Another result of rushing to compose an email. Sorry!

You are also using “dot shorthand” here to refer to an associated type
of IntegerLiteralConvertible. I think “dot shorthand” should be limited to
cases where there is only one protocol that is getting constrained. In
other cases, we need to be clear about which protocol we are referring to.

I borrowed dot shorthand from the generics manifesto. But you are right,
it should only be allowed if there is one protocol with associated types or
self requirements clause in the Any<...> construction.

I would actually go further and limit it to one protocol period, and
possibly even to one protocol and no type names (as types can have nested
types and typealiases). When we allow shorthand it should be immediately
unambiguous what the shorthand references with no need to look at type or
protocol declarations.

It might be desirable to propose the proposal with no allowance for
shorthand, and have the dot shorthand be a smaller follow-up proposal.

There will be rules to prevent recursive nesting. For example, if
generic typealiases are allowed, they cannot refer to each other in a
circular manner (like how structs can't contain themeselves, and you can't
create a cyclic graph of enums containing themselves).

How an existential can be used depends on what guarantees are provided
by the clauses. For example, 'Any<Equatable>' can't be used for much; if
there were any methods on Equatable that did not use the associated types
at all you'd be able to call them, but that's about it. However,
'Any<Equatable where .Self == String>' would allow for == to be called on
instances. (This is a stupid example, since Any<Equatable where .Self ==
> is equivalent to 'String', but there are almost certainly useful
examples one could come up with.)

In order of increasing 'power':

   - Don't constrain any associated types. You can pass around
   Any<Equatable>s, but that's about it.
   - Constrain associated types to conform to protocols.
   - Fully constrain associated types.

I think we need to spell out pretty clearly what members we expect to
be available or not available. This section probably needs the most design
and elaboration.

For example, we probably can’t access a member who uses an associated
type as an input unless it is constrained to a specific type. On the other
hand output types probably don’t need to limit access to a member.
However, if the output type is Self or an associated type the visible
signature would have an output type which has the relevant constraints of
the existential applied, but no more. In some cases this means the output
type would simply be Any.

Absolutely. This is vaguely what I had in mind but I wanted to get
something down first. Thanks for thinking through some of the implications
:).

That’s what I thought. Just wanted to start the process of elaborating
expectations.

Where this really gets tricky is for compound types like functions,
generic types, etc. Working out the details in these cases is pretty
complex. I will defer to Doug on whether it is best to just defer those
cases to the future, leave them up to the implementer, or try to work out
all of the relevant details in the proposal (in which case we probably need
a type system expert to help!).

Yes, exactly! For example, can Any<...> existentials involving protocols
with associated types or self requirements be used within generic function
or type definitions? Maybe there's an argument that existential types of
this nature are redundant if you have access to generics (e.g. defining a
property on a generic type that is a Collection containing Ints; you should
be able to do that today). On the other hand, maybe there are use cases I
haven't thought of…

I see no reason they shouldn’t be. They are not redundant at all. For
example, you may want to store instances in a heterogeneous collection.
You need existentials to do that.

A simple example of what I was referring to there is something like this:

protocol P {
    associatedtype Foo

    func bar(callback: (Foo) -> ())
}

In other words, types in the signature of a protocol member are complex
types that reference Self or associated types. I think you really need a
formal understanding of the type system to understand how to expose these
members through a constrained existential. We can probably understand the
expected behavior in some of the simpler cases on a case by case basis, but
that approach doesn’t scale at all and is arbitrary. If they’re going to
be supported an expert is going to need to be involved in the design.

Yes. I have some ideas regarding this topic.

One area you didn’t touch on is “opening” the existential? Is that out
of scope for this proposal? That would be fine with me as this proposal is
already taking on a lot. But if so, you should mention something about
future directions as it is pretty closely related to this proposal.

Yes, existential opening is explicitly separate from this (although I
wanted to mention it in the section where I talk about how Any<Equatable>
is not very useful). But you are absolutely right, this proposal should
discuss how it wants to interact with possible future directions.

Another area you didn’t touch on is whether Any constructs (and
typealiases referring to them) should be usable as generic constraints. I
would expect this to be possible but I think we need to spell it out.

I'm hoping for community input. This is a tricky subject, and at some
point we'll bump into implementation limitations.

I don’t think it’s too tricky. You can just unpack the constraints of
the Any into the list of generic constraints. Maybe I’m missing something,
but I don’t think so.

-Matthew

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

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

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

Okay now I feel like we’re merging everything we came up until now :D I’d love to see something like this happen to Swift, because `Any` seems to be a really powerful beast one day.

One quick question: Do we really need this "This must be the first requirement, if present.“?

I’m on the fence about this. The reason it would matter is for readability. The counter argument to that is that you can’t rely on the first argument being a class to determine whether a composed Any will have a class constraint or not.

···

On May 18, 2016, at 12:12 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

I’d say the compiler should reorder all types as it wants to. Current protocol<> already doing this today.

e.g.

protocol A {}
protocol B {}

typealias C = protocol<A, B>
typealias D = protocol<B, A>

print(C) // prints protocol<A, B>
print(D) // prints protocol<A, B>
print(C.self == D.self) // prints true

Basically what I mean

Any<SomeProtocol, class, AnotherProtocol>

Any<Any<ProtocolA, ProtocolB>, UIView>

should be valid.

--
Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 18:30:02, Austin Zheng via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

I made heavy revisions to my proposal to reflect all the great feedback I got from you and several other folks (https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md\).

Here are a couple of thoughts:

- I used 'requirements' because that's how the grammar describes similar constructs elsewhere in Swift. Open to change, though.

- Having only one where clause makes complete sense. Protocol extensions and generics all use one where clause, so should this construct.

- The "P can be used in lieu of Any<P>" just means you can declare e.g. "x : Equatable" if you want, instead of "x : Any<Equatable>", just like you can with protocols without associated types or self requirements today.

- I've come to the conclusion that it's probably best to propose the proposal in the most general form, and allow any reviewers on the core team to excise parts they don't think are useful enough or are too difficult to implement. In that spirit, the 'simple Any<...>' construct is gone and Any usage is fully general.

- I moved discussion of typealiases back into the main protocol, like you said, because typealiases using existentials are *not* actually generic and can be done today.

- I added some stuff about 'narrowing' existentials at point of use using as?. This isn't part of 'opening existentials' and should fit in this proposal nicely.

- I want a type expert to look at the 'usage' section, but I'm reasonably sure there's not much more flexibility we can give the user. Parameter associated types can't be treated as covariant (they are almost always invariant or contravariant IIRC) and therefore they should only be accessible if fully bound. Return types can be treated as covariant; some languages do and some don't. (Swift falls into the second bucket.) I would love to be wrong, though.

Austin

On May 18, 2016, at 8:45 AM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

Sent from my iPad

On May 18, 2016, at 2:35 AM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

I've put together a considerably more detailed draft proposal, taking into account as much of Matthew's feedback as I could. You can find it below:

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

Since there is no chance this will come up for review anytime soon, I expect to make significant revisions to it over the next month or so. Any feedback would be greatly appreciated.

Thank you for working on this! Great progress.

Minor nit, but I think the proper word is constraint rather than requirement here:

"Within the angle brackets < and > are zero or more requirements. Requirements are separated by commas."

Another tweak:

"P can be used in lieu of Any<P>, where P is a protocol with or without associated type or self requirements."

This proposal is introducing generalized existentials. P and Any<P> should be interchangeable for any protocol regardless of requirements of the protocol. Existentials of protocols with self or associated type requirements that do not include constraints will just expose limited direct functionality. It would still be possible to attempt cast them to concrete types to recover more functionality. In the future (after a follow on proposal) it will also be possible to open the existential.

Thorsten pointed out that there should only be one where clause for the whole existential. This follows the structure of generic type and function constraints. It may also be worth removing the 'as' alias from this proposal. This could be introduced as a stand alone proposal where it would apply to any context with generic constraints.

Another item:
// NOT ALLOWED let a : Any<Any<ProtocolA, ProtocolB>>

Why is this not allowed? It is pointless, but should be allowed and considered identical to the flattened syntax.

On dynamic casting, I don't believe it should be restricted in the way you have defined here. Casting *to* an existential doesn't have anything to do with opening an existential. We should allow casting to any existential type.

On a similar note, I completely disagree with the limitation you specify for use of Any in generic constraints precisely because of your counterargument. In the discussion about moving the where clause it has been noted that sometime it is necessary to apply a lot of constraints to get the necessary effect. A mechanism for factoring constraints is highly desirable and will greatly improve the readability of generic code. Typealiases bound to Any can provide such a mechanism. Let's not artificially restrict the use of it.

The section regarding members of a partly constrained existential needs to be more fleshed out. We can't simply punt it to a future proposal. However, I do think it is a good idea to wait until the core team has time to participate in the discussion.

The section about defining typealias also should not be left to the future. It is possible to define typealias with protocol<> today and to use that alias in a generic constraint. Removing that capability would be a regression. In fact, it's utility will increase significantly with this proposal.

In general, I don't think we need the distinction between simple and full Any. The whole idea of this proposal IMO should be fully generalizing existentials. If restrictions are necessary they should be due to (hopefully temporary) implementation considerations.

Austin

On Tue, May 17, 2016 at 9:52 PM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

On Tue, May 17, 2016 at 1:25 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

Within the angle brackets are zero or more 'clauses'. Clauses are separated by semicolons. (This is so commas can be used in where constraints, below. Better ideas are welcome. Maybe it's not necessary; we can use commas exclusively.)

I’m not a fan of the semicolon idea. I don’t see any reason for this. The `where` keyword separates the protocol list from the constraints just fine. The list on either side should be able to use commas with no problem (or line breaks if that proposal goes through).

I'm leaning towards getting rid of the commas, but would like to write out a few 'dummy' examples to see if there are any readability issues that arise.

Replaced with what? Whitespace separation? I suppose that might work for the protocol list but it feels inconsistent with the rest of Swift. Commas plus (hopefully) the alternative of newline seem like the right direction to me.

Sorry, I completely misspoke (mistyped?). I meant I want to get rid of the semicolons and use commas. I've come to the conclusion that there are no readability issues, protocol<> already uses commas, and semicolons used in this manner don't have a precedent anywhere else in the language.

There are five different possible clauses:

'class'. Must be the first clause, if present. Places a constraint on the existential to be any class type. (Implies: Only one can exist. Mutually exclusive with class name clause.)

(In the future a follow-up proposal should add in 'struct' or 'value' as a counterpart.)

If we’re going to allow `struct` we should also allow `enum`. `value` would allow either of those.

Of course. A future proposal can allow list members to discuss the exact details as to how struct, value, or enum specifiers should work.

Yep, agree. Just mentioning that if we’re going to reference it we should not leave obvious holes in what would be considered. :)

Absolutely.

Class name. Must be the first clause, if present. (Implies: Only one can exist. Mutually exclusive with 'class'.) Places a constraint on the existential (not really an existential anymore) to be an instance of the class, or one of its subclasses.

It is still be an existential if it includes protocol requirements that the class does not fulfill. For example, you might have Any<UIView, SomeProtocol> where UIView does not conform to SomeProtocol, but various subclasses do.

Fair enough. (I don't think the way things work would be affected.)

Your proposal doesn’t discuss composing Any in the way that Adrian’s did like this:

typealias Foo = Any<SomeClass, SomeProtocol, OtherProtocol>
Any<AnotherProtocol, Foo>

I didn't think it needed to be discussed. An Any<...> existential type is a type 'expression' just like any other, and should be allowed to participate in other Any<...>s.

I like the idea of composition as it allows us to factor out constraints. If we are going to do that we should allow a class to be specified in the composition as long is it is a subclass of all class requirements of Any types it composes. For example, this should be allowed:

typealias Bar = Any<SubclassOfSomeClass, Foo, AnotherProtocol>

This is still one class requirement for Bar, it just refines the class requirement of Foo to be SubclassOfSomeClass rather than just SomeClass.

This is a good point. There should be clarification as to how special cases of Any<...> used in another Any<...> behave. For example, like you said Any<MyClass, Any<SomeSubclassOfMyClass, Protocol>> should be valid. This will go into any proposal that emerges from the discussion.

Yes, this is why we need to discuss Any composition. There are also cases of incompatible associated type constraints which need to be rejected (such as composing two Any’s where one has Element == String and another has Element == Int).

Example: Any<UIViewController; UITableViewDataSource; UITableViewDelegate>
"Any UIViewController or subclass which also satisfies the table view data source and delegate protocols"
Dynamic protocol. This is entirely composed of the name of a protocol which has no associated types or Self requirement.
Example: Any<CustomStringConvertible; BooleanType>
"Any type which conforms to both the CustomStringConvertible and BooleanType protocols"

I'm going to use 'static protocol' to refer to a protocol with associated types or self requirements. Feel free to propose a more sound name.

Self-contained static protocol, simple. This is composed of the name of a static protocol, optionally followed by a 'where' clause in which the associated types can be constrained (with any of the three basic conformance types: subclassing, protocol conformance, or type equality). Associated types are referred to with a leading dot.

Please do not introduce terms “dynamic protocol” and “static protocol”. We want to support existentials of protocols that have self or associated type requirements. The dynamic vs static distinction is a limitation of the current implementation of Swift and doesn’t make sense for the long term vision.

I'm not trying to introduce new terms, these are just placeholders. At the same time "protocols with self or associated type requirements" is cumbersome to work with and it would be nice for someone to come up with a descriptive term of art for referring to them.

I agree that a better term would be useful. In the meantime, I would prefer something like “trivial” and “nontrivial” protocols.

I've decided to just use the full name until the community comes up with better names. Clarity is preferable to brevity in this case.

Example: Any<Collection where .Generator.Element : NSObject, .Generator.Element : SomeProtocol>
"Any type that is a Collection, whose elements are NSObjects or their subclasses conforming to SomeProtocol.”

Swift does not allow disjunction of requirements. Only conjunctions are supported. That means the correct reading is:

"Any type that is a Collection, whose elements are NSObjects and their subclasses conforming to SomeProtocol.”

Yes, that is what I meant. "whose elements are (NSObjects or their subclasses) conforming to SomeProtocol”.

Ok, good. Wasn’t quite clear to me.

Yes, the verbiage will need to be clearer in the future. That sentence could be ambiguously parsed.

Bound static protocol. This is the same as a self-contained static protocol, but with a leading "<name> as " which binds the protocol to a generic typealias. The name can be then be used in subsequent clauses to build constraints.

Example: Any<T as Collection; IntegerLiteralConvertible where .IntegerLiteralType == T.Element>.
"Any type that is a Collection, and also can be built from an integer literal, in which the collection elements are the same type as the type of the integer used for the integer literal conformance.”

I’m not sure about this, but if we’re going to do it it should be the other way around: `Collection as T` with the alias after the name of the protocol.

I like this, it flows better. "Protocol as T where Protocol.Foo == Int, Protocol.Bar : Baz”.

Why did you introduce an alias here and then not use it? Did you mean "Protocol as T where T.Foo == Int, T.Bar : Baz"

Another result of rushing to compose an email. Sorry!

You are also using “dot shorthand” here to refer to an associated type of IntegerLiteralConvertible. I think “dot shorthand” should be limited to cases where there is only one protocol that is getting constrained. In other cases, we need to be clear about which protocol we are referring to.

I borrowed dot shorthand from the generics manifesto. But you are right, it should only be allowed if there is one protocol with associated types or self requirements clause in the Any<...> construction.

I would actually go further and limit it to one protocol period, and possibly even to one protocol and no type names (as types can have nested types and typealiases). When we allow shorthand it should be immediately unambiguous what the shorthand references with no need to look at type or protocol declarations.

It might be desirable to propose the proposal with no allowance for shorthand, and have the dot shorthand be a smaller follow-up proposal.

There will be rules to prevent recursive nesting. For example, if generic typealiases are allowed, they cannot refer to each other in a circular manner (like how structs can't contain themeselves, and you can't create a cyclic graph of enums containing themselves).

How an existential can be used depends on what guarantees are provided by the clauses. For example, 'Any<Equatable>' can't be used for much; if there were any methods on Equatable that did not use the associated types at all you'd be able to call them, but that's about it. However, 'Any<Equatable where .Self == String>' would allow for == to be called on instances. (This is a stupid example, since Any<Equatable where .Self == String> is equivalent to 'String', but there are almost certainly useful examples one could come up with.)

In order of increasing 'power':
Don't constrain any associated types. You can pass around Any<Equatable>s, but that's about it.
Constrain associated types to conform to protocols.
Fully constrain associated types.

I think we need to spell out pretty clearly what members we expect to be available or not available. This section probably needs the most design and elaboration.

For example, we probably can’t access a member who uses an associated type as an input unless it is constrained to a specific type. On the other hand output types probably don’t need to limit access to a member. However, if the output type is Self or an associated type the visible signature would have an output type which has the relevant constraints of the existential applied, but no more. In some cases this means the output type would simply be Any.

Absolutely. This is vaguely what I had in mind but I wanted to get something down first. Thanks for thinking through some of the implications :).

That’s what I thought. Just wanted to start the process of elaborating expectations.

Where this really gets tricky is for compound types like functions, generic types, etc. Working out the details in these cases is pretty complex. I will defer to Doug on whether it is best to just defer those cases to the future, leave them up to the implementer, or try to work out all of the relevant details in the proposal (in which case we probably need a type system expert to help!).

Yes, exactly! For example, can Any<...> existentials involving protocols with associated types or self requirements be used within generic function or type definitions? Maybe there's an argument that existential types of this nature are redundant if you have access to generics (e.g. defining a property on a generic type that is a Collection containing Ints; you should be able to do that today). On the other hand, maybe there are use cases I haven't thought of…

I see no reason they shouldn’t be. They are not redundant at all. For example, you may want to store instances in a heterogeneous collection. You need existentials to do that.

A simple example of what I was referring to there is something like this:

protocol P {
    associatedtype Foo

    func bar(callback: (Foo) -> ())
}

In other words, types in the signature of a protocol member are complex types that reference Self or associated types. I think you really need a formal understanding of the type system to understand how to expose these members through a constrained existential. We can probably understand the expected behavior in some of the simpler cases on a case by case basis, but that approach doesn’t scale at all and is arbitrary. If they’re going to be supported an expert is going to need to be involved in the design.

Yes. I have some ideas regarding this topic.

One area you didn’t touch on is “opening” the existential? Is that out of scope for this proposal? That would be fine with me as this proposal is already taking on a lot. But if so, you should mention something about future directions as it is pretty closely related to this proposal.

Yes, existential opening is explicitly separate from this (although I wanted to mention it in the section where I talk about how Any<Equatable> is not very useful). But you are absolutely right, this proposal should discuss how it wants to interact with possible future directions.

Another area you didn’t touch on is whether Any constructs (and typealiases referring to them) should be usable as generic constraints. I would expect this to be possible but I think we need to spell it out.

I'm hoping for community input. This is a tricky subject, and at some point we'll bump into implementation limitations.

I don’t think it’s too tricky. You can just unpack the constraints of the Any into the list of generic constraints. Maybe I’m missing something, but I don’t think so.

-Matthew

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

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

So without any initial constraints how would one use this generic function??

extension UIButton: ProtocolA {}

let button = UIButton()
let shadowedButton: ProtocolA = UIButton()

// creates a set of a least one element if the generic type could be inferred
func unionIfPossible<T, U>(_ a: T, _ b: U) -> Set<Any<T, U>>? { /* merge somehow if possible */ }

// this should be valid because the compiler will assume Any<UIView, ProtocolA> where T == UIView and U == ProtocolA
let merged_1: Set<Any<UIView, ProtocolA>> = unionIfPossible( /* UIView subtype */ button, /* ProtocolA */ shadowedButton)

// this won’t be possible because of the restriction
let merged_2: Set<Any<UIView, ProtocolA>> = unionIfPossible(shadowedButton, button)

Any<UIView, ProtocolA> != Any<ProtocolA, UIView> isn’t right. Sure it may feel right for readability but the types should be equal.

"Can be any class type that is a UIView or a subclass of UIView, that also conforms to ProtocolA.“ == "Type that conforms to ProtocolA and that is a UIView or a subclass of UIView.“

This is also a nesting problem where you will be forced to choose the right place inside the angle brackets where to add a nested `Any<…>`.

class A: ClassB, ProtocolA {}

Any<A, Any<ClassB, ProtocolA>> == A != Any<Any<ClassB, ProtocolA>, A> == Any<ClassB, ProtocolA, A> which should be reorder by the compiler and inferred as A

···

--
Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 21:23:35, Austin Zheng (austinzheng@gmail.com) schrieb:

I strongly prefer keeping the class requirement first, both for readability and because this reflects how protocol and 'class' or superclass conformances already work when defining classes or protocols. I think "look at the first argument, and then recursively look at the first arguments of any nested Any's for class requirements" is straightforward enough not to confuse people, and helps keep the type definitions organized.

Austin

On Wed, May 18, 2016 at 12:11 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On May 18, 2016, at 12:12 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Okay now I feel like we’re merging everything we came up until now :D I’d love to see something like this happen to Swift, because `Any` seems to be a really powerful beast one day.

One quick question: Do we really need this "This must be the first requirement, if present.“?

I’m on the fence about this. The reason it would matter is for readability. The counter argument to that is that you can’t rely on the first argument being a class to determine whether a composed Any will have a class constraint or not.

I’d say the compiler should reorder all types as it wants to. Current protocol<> already doing this today.

e.g.

protocol A {}
protocol B {}

typealias C = protocol<A, B>
typealias D = protocol<B, A>

print(C) // prints protocol<A, B>
print(D) // prints protocol<A, B>
print(C.self == D.self) // prints true

Basically what I mean

Any<SomeProtocol, class, AnotherProtocol>

Any<Any<ProtocolA, ProtocolB>, UIView>

should be valid.

--
Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 18:30:02, Austin Zheng via swift-evolution (swift-evolution@swift.org) schrieb:

I made heavy revisions to my proposal to reflect all the great feedback I got from you and several other folks (https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md\).

Here are a couple of thoughts:

- I used 'requirements' because that's how the grammar describes similar constructs elsewhere in Swift. Open to change, though.

- Having only one where clause makes complete sense. Protocol extensions and generics all use one where clause, so should this construct.

- The "P can be used in lieu of Any<P>" just means you can declare e.g. "x : Equatable" if you want, instead of "x : Any<Equatable>", just like you can with protocols without associated types or self requirements today.

- I've come to the conclusion that it's probably best to propose the proposal in the most general form, and allow any reviewers on the core team to excise parts they don't think are useful enough or are too difficult to implement. In that spirit, the 'simple Any<...>' construct is gone and Any usage is fully general.

- I moved discussion of typealiases back into the main protocol, like you said, because typealiases using existentials are *not* actually generic and can be done today.

- I added some stuff about 'narrowing' existentials at point of use using as?. This isn't part of 'opening existentials' and should fit in this proposal nicely.

- I want a type expert to look at the 'usage' section, but I'm reasonably sure there's not much more flexibility we can give the user. Parameter associated types can't be treated as covariant (they are almost always invariant or contravariant IIRC) and therefore they should only be accessible if fully bound. Return types can be treated as covariant; some languages do and some don't. (Swift falls into the second bucket.) I would love to be wrong, though.

Austin

On May 18, 2016, at 8:45 AM, Matthew Johnson <matthew@anandabits.com> wrote:

Sent from my iPad

On May 18, 2016, at 2:35 AM, Austin Zheng <austinzheng@gmail.com> wrote:

I've put together a considerably more detailed draft proposal, taking into account as much of Matthew's feedback as I could. You can find it below:

Since there is no chance this will come up for review anytime soon, I expect to make significant revisions to it over the next month or so. Any feedback would be greatly appreciated.

Thank you for working on this! Great progress.

Minor nit, but I think the proper word is constraint rather than requirement here:

"Within the angle brackets < and > are zero or more requirements. Requirements are separated by commas."

Another tweak:

"P can be used in lieu of Any<P>, where P is a protocol with or without associated type or self requirements."

This proposal is introducing generalized existentials. P and Any<P> should be interchangeable for any protocol regardless of requirements of the protocol. Existentials of protocols with self or associated type requirements that do not include constraints will just expose limited direct functionality. It would still be possible to attempt cast them to concrete types to recover more functionality. In the future (after a follow on proposal) it will also be possible to open the existential.

Thorsten pointed out that there should only be one where clause for the whole existential. This follows the structure of generic type and function constraints. It may also be worth removing the 'as' alias from this proposal. This could be introduced as a stand alone proposal where it would apply to any context with generic constraints.

Another item:
// NOT ALLOWED let a : Any<Any<ProtocolA, ProtocolB>>

Why is this not allowed? It is pointless, but should be allowed and considered identical to the flattened syntax.

On dynamic casting, I don't believe it should be restricted in the way you have defined here. Casting *to* an existential doesn't have anything to do with opening an existential. We should allow casting to any existential type.

On a similar note, I completely disagree with the limitation you specify for use of Any in generic constraints precisely because of your counterargument. In the discussion about moving the where clause it has been noted that sometime it is necessary to apply a lot of constraints to get the necessary effect. A mechanism for factoring constraints is highly desirable and will greatly improve the readability of generic code. Typealiases bound to Any can provide such a mechanism. Let's not artificially restrict the use of it.

The section regarding members of a partly constrained existential needs to be more fleshed out. We can't simply punt it to a future proposal. However, I do think it is a good idea to wait until the core team has time to participate in the discussion.

The section about defining typealias also should not be left to the future. It is possible to define typealias with protocol<> today and to use that alias in a generic constraint. Removing that capability would be a regression. In fact, it's utility will increase significantly with this proposal.

In general, I don't think we need the distinction between simple and full Any. The whole idea of this proposal IMO should be fully generalizing existentials. If restrictions are necessary they should be due to (hopefully temporary) implementation considerations.

Austin

On Tue, May 17, 2016 at 9:52 PM, Austin Zheng <austinzheng@gmail.com> wrote:

On Tue, May 17, 2016 at 1:25 PM, Matthew Johnson <matthew@anandabits.com> wrote:

Within the angle brackets are zero or more 'clauses'. Clauses are separated by semicolons. (This is so commas can be used in where constraints, below. Better ideas are welcome. Maybe it's not necessary; we can use commas exclusively.)

I’m not a fan of the semicolon idea. I don’t see any reason for this. The `where` keyword separates the protocol list from the constraints just fine. The list on either side should be able to use commas with no problem (or line breaks if that proposal goes through).

I'm leaning towards getting rid of the commas, but would like to write out a few 'dummy' examples to see if there are any readability issues that arise.

Replaced with what? Whitespace separation? I suppose that might work for the protocol list but it feels inconsistent with the rest of Swift. Commas plus (hopefully) the alternative of newline seem like the right direction to me.

Sorry, I completely misspoke (mistyped?). I meant I want to get rid of the semicolons and use commas. I've come to the conclusion that there are no readability issues, protocol<> already uses commas, and semicolons used in this manner don't have a precedent anywhere else in the language.

There are five different possible clauses:

'class'. Must be the first clause, if present. Places a constraint on the existential to be any class type. (Implies: Only one can exist. Mutually exclusive with class name clause.)

(In the future a follow-up proposal should add in 'struct' or 'value' as a counterpart.)

If we’re going to allow `struct` we should also allow `enum`. `value` would allow either of those.

Of course. A future proposal can allow list members to discuss the exact details as to how struct, value, or enum specifiers should work.

Yep, agree. Just mentioning that if we’re going to reference it we should not leave obvious holes in what would be considered. :)

Absolutely.

Class name. Must be the first clause, if present. (Implies: Only one can exist. Mutually exclusive with 'class'.) Places a constraint on the existential (not really an existential anymore) to be an instance of the class, or one of its subclasses.
It is still be an existential if it includes protocol requirements that the class does not fulfill. For example, you might have Any<UIView, SomeProtocol> where UIView does not conform to SomeProtocol, but various subclasses do.

Fair enough. (I don't think the way things work would be affected.)

Your proposal doesn’t discuss composing Any in the way that Adrian’s did like this:

typealias Foo = Any<SomeClass, SomeProtocol, OtherProtocol>
Any<AnotherProtocol, Foo>

I didn't think it needed to be discussed. An Any<...> existential type is a type 'expression' just like any other, and should be allowed to participate in other Any<...>s.

I like the idea of composition as it allows us to factor out constraints. If we are going to do that we should allow a class to be specified in the composition as long is it is a subclass of all class requirements of Any types it composes. For example, this should be allowed:

typealias Bar = Any<SubclassOfSomeClass, Foo, AnotherProtocol>

This is still one class requirement for Bar, it just refines the class requirement of Foo to be SubclassOfSomeClass rather than just SomeClass.

This is a good point. There should be clarification as to how special cases of Any<...> used in another Any<...> behave. For example, like you said Any<MyClass, Any<SomeSubclassOfMyClass, Protocol>> should be valid. This will go into any proposal that emerges from the discussion.

Yes, this is why we need to discuss Any composition. There are also cases of incompatible associated type constraints which need to be rejected (such as composing two Any’s where one has Element == String and another has Element == Int).

Example: Any<UIViewController; UITableViewDataSource; UITableViewDelegate>
"Any UIViewController or subclass which also satisfies the table view data source and delegate protocols"
Dynamic protocol. This is entirely composed of the name of a protocol which has no associated types or Self requirement.
Example: Any<CustomStringConvertible; BooleanType>
"Any type which conforms to both the CustomStringConvertible and BooleanType protocols"

I'm going to use 'static protocol' to refer to a protocol with associated types or self requirements. Feel free to propose a more sound name.

Self-contained static protocol, simple. This is composed of the name of a static protocol, optionally followed by a 'where' clause in which the associated types can be constrained (with any of the three basic conformance types: subclassing, protocol conformance, or type equality). Associated types are referred to with a leading dot.
Please do not introduce terms “dynamic protocol” and “static protocol”. We want to support existentials of protocols that have self or associated type requirements. The dynamic vs static distinction is a limitation of the current implementation of Swift and doesn’t make sense for the long term vision.

I'm not trying to introduce new terms, these are just placeholders. At the same time "protocols with self or associated type requirements" is cumbersome to work with and it would be nice for someone to come up with a descriptive term of art for referring to them.

I agree that a better term would be useful. In the meantime, I would prefer something like “trivial” and “nontrivial” protocols.

I've decided to just use the full name until the community comes up with better names. Clarity is preferable to brevity in this case.

Example: Any<Collection where .Generator.Element : NSObject, .Generator.Element : SomeProtocol>
"Any type that is a Collection, whose elements are NSObjects or their subclasses conforming to SomeProtocol.”

Swift does not allow disjunction of requirements. Only conjunctions are supported. That means the correct reading is:

"Any type that is a Collection, whose elements are NSObjects and their subclasses conforming to SomeProtocol.”

Yes, that is what I meant. "whose elements are (NSObjects or their subclasses) conforming to SomeProtocol”.

Ok, good. Wasn’t quite clear to me.

Yes, the verbiage will need to be clearer in the future. That sentence could be ambiguously parsed.

Bound static protocol. This is the same as a self-contained static protocol, but with a leading "<name> as " which binds the protocol to a generic typealias. The name can be then be used in subsequent clauses to build constraints.

Example: Any<T as Collection; IntegerLiteralConvertible where .IntegerLiteralType == T.Element>.
"Any type that is a Collection, and also can be built from an integer literal, in which the collection elements are the same type as the type of the integer used for the integer literal conformance.”

I’m not sure about this, but if we’re going to do it it should be the other way around: `Collection as T` with the alias after the name of the protocol.

I like this, it flows better. "Protocol as T where Protocol.Foo == Int, Protocol.Bar : Baz”.

Why did you introduce an alias here and then not use it? Did you mean "Protocol as T where T.Foo == Int, T.Bar : Baz"

Another result of rushing to compose an email. Sorry!

You are also using “dot shorthand” here to refer to an associated type of IntegerLiteralConvertible. I think “dot shorthand” should be limited to cases where there is only one protocol that is getting constrained. In other cases, we need to be clear about which protocol we are referring to.

I borrowed dot shorthand from the generics manifesto. But you are right, it should only be allowed if there is one protocol with associated types or self requirements clause in the Any<...> construction.

I would actually go further and limit it to one protocol period, and possibly even to one protocol and no type names (as types can have nested types and typealiases). When we allow shorthand it should be immediately unambiguous what the shorthand references with no need to look at type or protocol declarations.

It might be desirable to propose the proposal with no allowance for shorthand, and have the dot shorthand be a smaller follow-up proposal.

There will be rules to prevent recursive nesting. For example, if generic typealiases are allowed, they cannot refer to each other in a circular manner (like how structs can't contain themeselves, and you can't create a cyclic graph of enums containing themselves).

How an existential can be used depends on what guarantees are provided by the clauses. For example, 'Any<Equatable>' can't be used for much; if there were any methods on Equatable that did not use the associated types at all you'd be able to call them, but that's about it. However, 'Any<Equatable where .Self == String>' would allow for == to be called on instances. (This is a stupid example, since Any<Equatable where .Self == String> is equivalent to 'String', but there are almost certainly useful examples one could come up with.)

In order of increasing 'power':
Don't constrain any associated types. You can pass around Any<Equatable>s, but that's about it.
Constrain associated types to conform to protocols.
Fully constrain associated types.
I think we need to spell out pretty clearly what members we expect to be available or not available. This section probably needs the most design and elaboration.

For example, we probably can’t access a member who uses an associated type as an input unless it is constrained to a specific type. On the other hand output types probably don’t need to limit access to a member. However, if the output type is Self or an associated type the visible signature would have an output type which has the relevant constraints of the existential applied, but no more. In some cases this means the output type would simply be Any.

Absolutely. This is vaguely what I had in mind but I wanted to get something down first. Thanks for thinking through some of the implications :).

That’s what I thought. Just wanted to start the process of elaborating expectations.

Where this really gets tricky is for compound types like functions, generic types, etc. Working out the details in these cases is pretty complex. I will defer to Doug on whether it is best to just defer those cases to the future, leave them up to the implementer, or try to work out all of the relevant details in the proposal (in which case we probably need a type system expert to help!).

Yes, exactly! For example, can Any<...> existentials involving protocols with associated types or self requirements be used within generic function or type definitions? Maybe there's an argument that existential types of this nature are redundant if you have access to generics (e.g. defining a property on a generic type that is a Collection containing Ints; you should be able to do that today). On the other hand, maybe there are use cases I haven't thought of…

I see no reason they shouldn’t be. They are not redundant at all. For example, you may want to store instances in a heterogeneous collection. You need existentials to do that.

A simple example of what I was referring to there is something like this:

protocol P {
associatedtype Foo

func bar\(callback: \(Foo\) \-&gt; \(\)\)

}

In other words, types in the signature of a protocol member are complex types that reference Self or associated types. I think you really need a formal understanding of the type system to understand how to expose these members through a constrained existential. We can probably understand the expected behavior in some of the simpler cases on a case by case basis, but that approach doesn’t scale at all and is arbitrary. If they’re going to be supported an expert is going to need to be involved in the design.

Yes. I have some ideas regarding this topic.

One area you didn’t touch on is “opening” the existential? Is that out of scope for this proposal? That would be fine with me as this proposal is already taking on a lot. But if so, you should mention something about future directions as it is pretty closely related to this proposal.

Yes, existential opening is explicitly separate from this (although I wanted to mention it in the section where I talk about how Any<Equatable> is not very useful). But you are absolutely right, this proposal should discuss how it wants to interact with possible future directions.

Another area you didn’t touch on is whether Any constructs (and typealiases referring to them) should be usable as generic constraints. I would expect this to be possible but I think we need to spell it out.

I'm hoping for community input. This is a tricky subject, and at some point we'll bump into implementation limitations.

I don’t think it’s too tricky. You can just unpack the constraints of the Any into the list of generic constraints. Maybe I’m missing something, but I don’t think so.

-Matthew

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

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

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.

It turns out the runtime type system is considerably more powerful than I
expected. The previous concept in which protocols with associated types'
APIs were vended out selectively and using existentials has been discarded.

Instead, all the associated types that belong to an existential are
accessible as 'anonymous' types within the scope of the existential. These
anonymous types are not existentials - they are an anonymous representation
of whatever concrete type is satisfying the existential's value's
underlying type's associated type.

This is an enormous step up in power - for example, an existential can
return a value of one of these anonymous associated types from one function
and pass it into another function that takes the same type, maintaining
perfect type safety but without ever revealing the actual type. There is no
need anymore to limit the APIs exposed to the user, although there may
still exist APIs that are semantically useless without additional type
information.

A set of conversions has also been defined. At compile-time 'as' can be
used to turn values of these anonymous associated types back into
existentials based on the constraints defined earlier. 'as?' can also be
used for conditional casting of these anonymously-typed values into
potential actual types.

As always, the link is here, and feedback would be greatly appreciated:

Best,
Austin

···

On Tue, May 24, 2016 at 5:09 AM, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

Sent from my iPad

On May 23, 2016, at 9:52 PM, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> wrote:

>> One initial bit of feedback - I believe if you have existential types,
I believe you can define Sequence Element directly, rather than with a type
alias. e.g.
>>
>> protocol Sequence {
>> associatedtype Element
>> associatedtype Iterator: any<IteratorProtocol where
IteratorProtocol.Element==Element>
>> associatedtype SubSequence: any<Sequence where Sequence.Element ==
>
>> …
>> }
>
> That's not really the same thing. Any<IteratorProtocol> is an
existential, not a protocol. It's basically an automatically-generated
version of our current `AnyIterator<T>` type (though with some additional
flexibility). It can't appear on the right side of a `:`, any more than
AnyIterator could.

After this proposal you should be able to use these existentials anywhere
you can place a constraint, so it would work. You can do this with the
protocol composition operator today and the future existential is just an
extension of that capability.

>
> What *would* work is allowing `where` clauses on associated types:
>
>> protocol Sequence {
>> associatedtype Element
>> associatedtype Iterator: IteratorProtocol where
Iterator.Element==Element
>> associatedtype SubSequence: Sequence where SubSequence.Element ==
Element
>> …
>> }
>
> I believe this is part of the generics manifesto.
>
> --
> Brent Royal-Gordon
> Architechies
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

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

Sent from my iPad

I've put together a considerably more detailed draft proposal, taking into account as much of Matthew's feedback as I could. You can find it below:

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

Since there is no chance this will come up for review anytime soon, I expect to make significant revisions to it over the next month or so. Any feedback would be greatly appreciated.

This is very much Swift 4 territory, but I can’t help myself… so…

The actual feature description is spread out through this very long document, with user-facing ideas (e.g., using “anonymous associated types”) intermixed with deeper technical details (existential type equivalence), so it’s very daunting to read. Please bring the user-facing features to the front (“Proposed Solution”) with examples, and save the deeper technical details for “Detailed Design”. You want more readers to make it through the part that affects them.

Shortcut 'dot' notation: If there is only one protocol with associated types specified in the requirements, and there are no nested Any<...> requirements with where clauses of their own, that protocol's name can be omitted from the whereclause constraints:

// Okay
// Would otherwise be Any< ~ where Collection.Element == Int>
let a : Any<class, Collection, Any<Streamable, CustomStringConvertible> where .Element == Int>

// NOT ALLOWED
// Both Collection and OptionSetType have associated types.
let b : Any<Collection, OptionSetType where .Element == Int>
FWIW, I think “.Element == Int” should be the only syntax. In generic signatures, if you have two different protocols with same-named associated types, and a given type parameter (or associated type) conforms to both protocols, the associated types are (implicitly) made equivalent via an inferred same-type constraint. So there’s no reason to introduce the “Collection.Element == Int” syntax, because the “Collection” part is basically irrelevant.

Once existentials have been suitably enhanced, there is a strong analogy between an existential and a generic signature with a single type parameter that you can’t name. An existential Any<Collection where .Element : Equatable> has most of the same characteristics as a generic something with the signature <T : Collection where T.Element : Equatable>. Specifically, the sections on “Existential type equivalence”, “Ordering”, “Real types to anonymous associated types”, “Anonymous associated types to real types”. could be reduced to a few small, simple examples and a mention of the analogous behavior of generics. It will be far easier to explain this way, and readers don’t need to get immersed in the details. Where there are differences vs. generics, that’s important to point out.

“Associated typealias rewriting”: this also falls out of the equivalence with generics + SE-0092.

“Associated types and member exposure”: you don’t make the point that it only makes sense to refer to the associated types of a let constant; a var could change its type dynamically, which would invalidate the typing rules. Did you consider just using “x.dynamicType” in the type grammar for this? It’s more general, in that you can refer to associated types but also talk about the dynamic type of “x” itself, e.g.,

  let x: Equatable = …
  let y: Equatable = …
  if let yAsX = y as? x.dynamicType { … x == yAsX … }

which is (almost?) as powerful as a general “open” expression.

I’m not a fan of the “anonymous associated types” terminology: these are associated types of a type of some runtime-defined value. The only thing “anonymous” about them is that it’s harder to spell the base type; otherwise, they’re just like associated types of a generic type parameter. Again, the generics analogy is strong here.

FWIW, I don’t think we’ll ever need “opening existentials” with what you’ve described here. Also, remember that a method of a protocol extension essentially opens “Self”, so we already have one way to open an existential (and that’s probably enough).

I was a little surprised you didn’t point out that AnyObject could become

  typealias AnyObject = Any<class>

or give the nice “AnyCollection” syntax:

  typealias AnyCollection<T> = Any<Collection where .Element == T>

the latter of which is fairly important, because it gives nice syntactic sure to one of the most highly-requested features [*]. I’d suggest having that example very, very early.

  - Doug

[*] That generally comes in as “Swift should have parameterized protocols…”

Great feedback here Doug.

FWIW, we also occasionally get "Swift should have parameterized protocols" in the context of multiple conformances by the same concrete type (as in things like ConvertibleTo<T> protocol).

I know. From the bugs I've seen it's at least 10x as many requests for "any collection of some element type" as for any actually reason why one would need parameterize a protocols.

  - Doug

···

Sent from my iPhone

On Jun 5, 2016, at 6:41 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On Jun 5, 2016, at 6:20 PM, Douglas Gregor <dgregor@apple.com> wrote:

On May 18, 2016, at 12:35 AM, Austin Zheng <austinzheng@gmail.com> wrote:

Austin

On Tue, May 17, 2016 at 9:52 PM, Austin Zheng <austinzheng@gmail.com> wrote:

On Tue, May 17, 2016 at 1:25 PM, Matthew Johnson <matthew@anandabits.com> wrote:

Within the angle brackets are zero or more 'clauses'. Clauses are separated by semicolons. (This is so commas can be used in where constraints, below. Better ideas are welcome. Maybe it's not necessary; we can use commas exclusively.)

I’m not a fan of the semicolon idea. I don’t see any reason for this. The `where` keyword separates the protocol list from the constraints just fine. The list on either side should be able to use commas with no problem (or line breaks if that proposal goes through).

I'm leaning towards getting rid of the commas, but would like to write out a few 'dummy' examples to see if there are any readability issues that arise.

Replaced with what? Whitespace separation? I suppose that might work for the protocol list but it feels inconsistent with the rest of Swift. Commas plus (hopefully) the alternative of newline seem like the right direction to me.

Sorry, I completely misspoke (mistyped?). I meant I want to get rid of the semicolons and use commas. I've come to the conclusion that there are no readability issues, protocol<> already uses commas, and semicolons used in this manner don't have a precedent anywhere else in the language.

There are five different possible clauses:

'class'. Must be the first clause, if present. Places a constraint on the existential to be any class type. (Implies: Only one can exist. Mutually exclusive with class name clause.)

(In the future a follow-up proposal should add in 'struct' or 'value' as a counterpart.)

If we’re going to allow `struct` we should also allow `enum`. `value` would allow either of those.

Of course. A future proposal can allow list members to discuss the exact details as to how struct, value, or enum specifiers should work.

Yep, agree. Just mentioning that if we’re going to reference it we should not leave obvious holes in what would be considered. :)

Absolutely.

Class name. Must be the first clause, if present. (Implies: Only one can exist. Mutually exclusive with 'class'.) Places a constraint on the existential (not really an existential anymore) to be an instance of the class, or one of its subclasses.

It is still be an existential if it includes protocol requirements that the class does not fulfill. For example, you might have Any<UIView, SomeProtocol> where UIView does not conform to SomeProtocol, but various subclasses do.

Fair enough. (I don't think the way things work would be affected.)

Your proposal doesn’t discuss composing Any in the way that Adrian’s did like this:

typealias Foo = Any<SomeClass, SomeProtocol, OtherProtocol>
Any<AnotherProtocol, Foo>

I didn't think it needed to be discussed. An Any<...> existential type is a type 'expression' just like any other, and should be allowed to participate in other Any<...>s.

I like the idea of composition as it allows us to factor out constraints. If we are going to do that we should allow a class to be specified in the composition as long is it is a subclass of all class requirements of Any types it composes. For example, this should be allowed:

typealias Bar = Any<SubclassOfSomeClass, Foo, AnotherProtocol>

This is still one class requirement for Bar, it just refines the class requirement of Foo to be SubclassOfSomeClass rather than just SomeClass.

This is a good point. There should be clarification as to how special cases of Any<...> used in another Any<...> behave. For example, like you said Any<MyClass, Any<SomeSubclassOfMyClass, Protocol>> should be valid. This will go into any proposal that emerges from the discussion.

Yes, this is why we need to discuss Any composition. There are also cases of incompatible associated type constraints which need to be rejected (such as composing two Any’s where one has Element == String and another has Element == Int).

Example: Any<UIViewController; UITableViewDataSource; UITableViewDelegate>
"Any UIViewController or subclass which also satisfies the table view data source and delegate protocols"
Dynamic protocol. This is entirely composed of the name of a protocol which has no associated types or Self requirement.
Example: Any<CustomStringConvertible; BooleanType>
"Any type which conforms to both the CustomStringConvertible and BooleanType protocols"

I'm going to use 'static protocol' to refer to a protocol with associated types or self requirements. Feel free to propose a more sound name.

Self-contained static protocol, simple. This is composed of the name of a static protocol, optionally followed by a 'where' clause in which the associated types can be constrained (with any of the three basic conformance types: subclassing, protocol conformance, or type equality). Associated types are referred to with a leading dot.

Please do not introduce terms “dynamic protocol” and “static protocol”. We want to support existentials of protocols that have self or associated type requirements. The dynamic vs static distinction is a limitation of the current implementation of Swift and doesn’t make sense for the long term vision.

I'm not trying to introduce new terms, these are just placeholders. At the same time "protocols with self or associated type requirements" is cumbersome to work with and it would be nice for someone to come up with a descriptive term of art for referring to them.

I agree that a better term would be useful. In the meantime, I would prefer something like “trivial” and “nontrivial” protocols.

I've decided to just use the full name until the community comes up with better names. Clarity is preferable to brevity in this case.

Example: Any<Collection where .Generator.Element : NSObject, .Generator.Element : SomeProtocol>
"Any type that is a Collection, whose elements are NSObjects or their subclasses conforming to SomeProtocol.”

Swift does not allow disjunction of requirements. Only conjunctions are supported. That means the correct reading is:

"Any type that is a Collection, whose elements are NSObjects and their subclasses conforming to SomeProtocol.”

Yes, that is what I meant. "whose elements are (NSObjects or their subclasses) conforming to SomeProtocol”.

Ok, good. Wasn’t quite clear to me.

Yes, the verbiage will need to be clearer in the future. That sentence could be ambiguously parsed.

Bound static protocol. This is the same as a self-contained static protocol, but with a leading "<name> as " which binds the protocol to a generic typealias. The name can be then be used in subsequent clauses to build constraints.

Example: Any<T as Collection; IntegerLiteralConvertible where .IntegerLiteralType == T.Element>.
"Any type that is a Collection, and also can be built from an integer literal, in which the collection elements are the same type as the type of the integer used for the integer literal conformance.”

I’m not sure about this, but if we’re going to do it it should be the other way around: `Collection as T` with the alias after the name of the protocol.

I like this, it flows better. "Protocol as T where Protocol.Foo == Int, Protocol.Bar : Baz”.

Why did you introduce an alias here and then not use it? Did you mean "Protocol as T where T.Foo == Int, T.Bar : Baz"

Another result of rushing to compose an email. Sorry!

You are also using “dot shorthand” here to refer to an associated type of IntegerLiteralConvertible. I think “dot shorthand” should be limited to cases where there is only one protocol that is getting constrained. In other cases, we need to be clear about which protocol we are referring to.

I borrowed dot shorthand from the generics manifesto. But you are right, it should only be allowed if there is one protocol with associated types or self requirements clause in the Any<...> construction.

I would actually go further and limit it to one protocol period, and possibly even to one protocol and no type names (as types can have nested types and typealiases). When we allow shorthand it should be immediately unambiguous what the shorthand references with no need to look at type or protocol declarations.

It might be desirable to propose the proposal with no allowance for shorthand, and have the dot shorthand be a smaller follow-up proposal.

There will be rules to prevent recursive nesting. For example, if generic typealiases are allowed, they cannot refer to each other in a circular manner (like how structs can't contain themeselves, and you can't create a cyclic graph of enums containing themselves).

How an existential can be used depends on what guarantees are provided by the clauses. For example, 'Any<Equatable>' can't be used for much; if there were any methods on Equatable that did not use the associated types at all you'd be able to call them, but that's about it. However, 'Any<Equatable where .Self == String>' would allow for == to be called on instances. (This is a stupid example, since Any<Equatable where .Self == String> is equivalent to 'String', but there are almost certainly useful examples one could come up with.)

In order of increasing 'power':
Don't constrain any associated types. You can pass around Any<Equatable>s, but that's about it.
Constrain associated types to conform to protocols.
Fully constrain associated types.

I think we need to spell out pretty clearly what members we expect to be available or not available. This section probably needs the most design and elaboration.

For example, we probably can’t access a member who uses an associated type as an input unless it is constrained to a specific type. On the other hand output types probably don’t need to limit access to a member. However, if the output type is Self or an associated type the visible signature would have an output type which has the relevant constraints of the existential applied, but no more. In some cases this means the output type would simply be Any.

Absolutely. This is vaguely what I had in mind but I wanted to get something down first. Thanks for thinking through some of the implications :).

That’s what I thought. Just wanted to start the process of elaborating expectations.

Where this really gets tricky is for compound types like functions, generic types, etc. Working out the details in these cases is pretty complex. I will defer to Doug on whether it is best to just defer those cases to the future, leave them up to the implementer, or try to work out all of the relevant details in the proposal (in which case we probably need a type system expert to help!).

Yes, exactly! For example, can Any<...> existentials involving protocols with associated types or self requirements be used within generic function or type definitions? Maybe there's an argument that existential types of this nature are redundant if you have access to generics (e.g. defining a property on a generic type that is a Collection containing Ints; you should be able to do that today). On the other hand, maybe there are use cases I haven't thought of…

I see no reason they shouldn’t be. They are not redundant at all. For example, you may want to store instances in a heterogeneous collection. You need existentials to do that.

A simple example of what I was referring to there is something like this:

protocol P {
    associatedtype Foo

    func bar(callback: (Foo) -> ())
}

In other words, types in the signature of a protocol member are complex types that reference Self or associated types. I think you really need a formal understanding of the type system to understand how to expose these members through a constrained existential. We can probably understand the expected behavior in some of the simpler cases on a case by case basis, but that approach doesn’t scale at all and is arbitrary. If they’re going to be supported an expert is going to need to be involved in the design.

Yes. I have some ideas regarding this topic.

One area you didn’t touch on is “opening” the existential? Is that out of scope for this proposal? That would be fine with me as this proposal is already taking on a lot. But if so, you should mention something about future directions as it is pretty closely related to this proposal.

Yes, existential opening is explicitly separate from this (although I wanted to mention it in the section where I talk about how Any<Equatable> is not very useful). But you are absolutely right, this proposal should discuss how it wants to interact with possible future directions.

Another area you didn’t touch on is whether Any constructs (and typealiases referring to them) should be usable as generic constraints. I would expect this to be possible but I think we need to spell it out.

I'm hoping for community input. This is a tricky subject, and at some point we'll bump into implementation limitations.

I don’t think it’s too tricky. You can just unpack the constraints of the Any into the list of generic constraints. Maybe I’m missing something, but I don’t think so.

-Matthew

Thank you for your feedback, Doug! I appreciate you taking the time to read through everything and write up your thoughts. I'll revise the proposal and plan for a 2017 time frame.

It should be sooner than that.

I would also be happy to see someone from the core team write up a proposal when the time is right, should they feel this one isn't in the spirit of the desired feature.

I think you're on the right track and don't want to duplicate effort.

  - Doug

···

Sent from my iPhone

On Jun 5, 2016, at 7:02 PM, Austin Zheng <austinzheng@gmail.com> wrote:

Austin

On Sun, Jun 5, 2016 at 4:20 PM, Douglas Gregor <dgregor@apple.com> wrote:

On May 18, 2016, at 12:35 AM, Austin Zheng <austinzheng@gmail.com> wrote:

I've put together a considerably more detailed draft proposal, taking into account as much of Matthew's feedback as I could. You can find it below:

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

Since there is no chance this will come up for review anytime soon, I expect to make significant revisions to it over the next month or so. Any feedback would be greatly appreciated.

This is very much Swift 4 territory, but I can’t help myself… so…

The actual feature description is spread out through this very long document, with user-facing ideas (e.g., using “anonymous associated types”) intermixed with deeper technical details (existential type equivalence), so it’s very daunting to read. Please bring the user-facing features to the front (“Proposed Solution”) with examples, and save the deeper technical details for “Detailed Design”. You want more readers to make it through the part that affects them.

Shortcut 'dot' notation: If there is only one protocol with associated types specified in the requirements, and there are no nested Any<...> requirements with where clauses of their own, that protocol's name can be omitted from the whereclause constraints:

// Okay
// Would otherwise be Any< ~ where Collection.Element == Int>
let a : Any<class, Collection, Any<Streamable, CustomStringConvertible> where .Element == Int>

// NOT ALLOWED
// Both Collection and OptionSetType have associated types.
let b : Any<Collection, OptionSetType where .Element == Int>
FWIW, I think “.Element == Int” should be the only syntax. In generic signatures, if you have two different protocols with same-named associated types, and a given type parameter (or associated type) conforms to both protocols, the associated types are (implicitly) made equivalent via an inferred same-type constraint. So there’s no reason to introduce the “Collection.Element == Int” syntax, because the “Collection” part is basically irrelevant.

Once existentials have been suitably enhanced, there is a strong analogy between an existential and a generic signature with a single type parameter that you can’t name. An existential Any<Collection where .Element : Equatable> has most of the same characteristics as a generic something with the signature <T : Collection where T.Element : Equatable>. Specifically, the sections on “Existential type equivalence”, “Ordering”, “Real types to anonymous associated types”, “Anonymous associated types to real types”. could be reduced to a few small, simple examples and a mention of the analogous behavior of generics. It will be far easier to explain this way, and readers don’t need to get immersed in the details. Where there are differences vs. generics, that’s important to point out.

“Associated typealias rewriting”: this also falls out of the equivalence with generics + SE-0092.

“Associated types and member exposure”: you don’t make the point that it only makes sense to refer to the associated types of a let constant; a var could change its type dynamically, which would invalidate the typing rules. Did you consider just using “x.dynamicType” in the type grammar for this? It’s more general, in that you can refer to associated types but also talk about the dynamic type of “x” itself, e.g.,

  let x: Equatable = …
  let y: Equatable = …
  if let yAsX = y as? x.dynamicType { … x == yAsX … }

which is (almost?) as powerful as a general “open” expression.

I’m not a fan of the “anonymous associated types” terminology: these are associated types of a type of some runtime-defined value. The only thing “anonymous” about them is that it’s harder to spell the base type; otherwise, they’re just like associated types of a generic type parameter. Again, the generics analogy is strong here.

FWIW, I don’t think we’ll ever need “opening existentials” with what you’ve described here. Also, remember that a method of a protocol extension essentially opens “Self”, so we already have one way to open an existential (and that’s probably enough).

I was a little surprised you didn’t point out that AnyObject could become

  typealias AnyObject = Any<class>

or give the nice “AnyCollection” syntax:

  typealias AnyCollection<T> = Any<Collection where .Element == T>

the latter of which is fairly important, because it gives nice syntactic sure to one of the most highly-requested features [*]. I’d suggest having that example very, very early.

  - Doug

[*] That generally comes in as “Swift should have parameterized protocols…”

Austin

On Tue, May 17, 2016 at 9:52 PM, Austin Zheng <austinzheng@gmail.com> wrote:

On Tue, May 17, 2016 at 1:25 PM, Matthew Johnson <matthew@anandabits.com> wrote:

Within the angle brackets are zero or more 'clauses'. Clauses are separated by semicolons. (This is so commas can be used in where constraints, below. Better ideas are welcome. Maybe it's not necessary; we can use commas exclusively.)

I’m not a fan of the semicolon idea. I don’t see any reason for this. The `where` keyword separates the protocol list from the constraints just fine. The list on either side should be able to use commas with no problem (or line breaks if that proposal goes through).

I'm leaning towards getting rid of the commas, but would like to write out a few 'dummy' examples to see if there are any readability issues that arise.

Replaced with what? Whitespace separation? I suppose that might work for the protocol list but it feels inconsistent with the rest of Swift. Commas plus (hopefully) the alternative of newline seem like the right direction to me.

Sorry, I completely misspoke (mistyped?). I meant I want to get rid of the semicolons and use commas. I've come to the conclusion that there are no readability issues, protocol<> already uses commas, and semicolons used in this manner don't have a precedent anywhere else in the language.

There are five different possible clauses:

'class'. Must be the first clause, if present. Places a constraint on the existential to be any class type. (Implies: Only one can exist. Mutually exclusive with class name clause.)

(In the future a follow-up proposal should add in 'struct' or 'value' as a counterpart.)

If we’re going to allow `struct` we should also allow `enum`. `value` would allow either of those.

Of course. A future proposal can allow list members to discuss the exact details as to how struct, value, or enum specifiers should work.

Yep, agree. Just mentioning that if we’re going to reference it we should not leave obvious holes in what would be considered. :)

Absolutely.

Class name. Must be the first clause, if present. (Implies: Only one can exist. Mutually exclusive with 'class'.) Places a constraint on the existential (not really an existential anymore) to be an instance of the class, or one of its subclasses.

It is still be an existential if it includes protocol requirements that the class does not fulfill. For example, you might have Any<UIView, SomeProtocol> where UIView does not conform to SomeProtocol, but various subclasses do.

Fair enough. (I don't think the way things work would be affected.)

Your proposal doesn’t discuss composing Any in the way that Adrian’s did like this:

typealias Foo = Any<SomeClass, SomeProtocol, OtherProtocol>
Any<AnotherProtocol, Foo>

I didn't think it needed to be discussed. An Any<...> existential type is a type 'expression' just like any other, and should be allowed to participate in other Any<...>s.

I like the idea of composition as it allows us to factor out constraints. If we are going to do that we should allow a class to be specified in the composition as long is it is a subclass of all class requirements of Any types it composes. For example, this should be allowed:

typealias Bar = Any<SubclassOfSomeClass, Foo, AnotherProtocol>

This is still one class requirement for Bar, it just refines the class requirement of Foo to be SubclassOfSomeClass rather than just SomeClass.

This is a good point. There should be clarification as to how special cases of Any<...> used in another Any<...> behave. For example, like you said Any<MyClass, Any<SomeSubclassOfMyClass, Protocol>> should be valid. This will go into any proposal that emerges from the discussion.

Yes, this is why we need to discuss Any composition. There are also cases of incompatible associated type constraints which need to be rejected (such as composing two Any’s where one has Element == String and another has Element == Int).

Example: Any<UIViewController; UITableViewDataSource; UITableViewDelegate>
"Any UIViewController or subclass which also satisfies the table view data source and delegate protocols"
Dynamic protocol. This is entirely composed of the name of a protocol which has no associated types or Self requirement.
Example: Any<CustomStringConvertible; BooleanType>
"Any type which conforms to both the CustomStringConvertible and BooleanType protocols"

I'm going to use 'static protocol' to refer to a protocol with associated types or self requirements. Feel free to propose a more sound name.

Self-contained static protocol, simple. This is composed of the name of a static protocol, optionally followed by a 'where' clause in which the associated types can be constrained (with any of the three basic conformance types: subclassing, protocol conformance, or type equality). Associated types are referred to with a leading dot.

Please do not introduce terms “dynamic protocol” and “static protocol”. We want to support existentials of protocols that have self or associated type requirements. The dynamic vs static distinction is a limitation of the current implementation of Swift and doesn’t make sense for the long term vision.

I'm not trying to introduce new terms, these are just placeholders. At the same time "protocols with self or associated type requirements" is cumbersome to work with and it would be nice for someone to come up with a descriptive term of art for referring to them.

I agree that a better term would be useful. In the meantime, I would prefer something like “trivial” and “nontrivial” protocols.

I've decided to just use the full name until the community comes up with better names. Clarity is preferable to brevity in this case.

Example: Any<Collection where .Generator.Element : NSObject, .Generator.Element : SomeProtocol>
"Any type that is a Collection, whose elements are NSObjects or their subclasses conforming to SomeProtocol.”

Swift does not allow disjunction of requirements. Only conjunctions are supported. That means the correct reading is:

"Any type that is a Collection, whose elements are NSObjects and their subclasses conforming to SomeProtocol.”

Yes, that is what I meant. "whose elements are (NSObjects or their subclasses) conforming to SomeProtocol”.

Ok, good. Wasn’t quite clear to me.

Yes, the verbiage will need to be clearer in the future. That sentence could be ambiguously parsed.

Bound static protocol. This is the same as a self-contained static protocol, but with a leading "<name> as " which binds the protocol to a generic typealias. The name can be then be used in subsequent clauses to build constraints.

Example: Any<T as Collection; IntegerLiteralConvertible where .IntegerLiteralType == T.Element>.
"Any type that is a Collection, and also can be built from an integer literal, in which the collection elements are the same type as the type of the integer used for the integer literal conformance.”

I’m not sure about this, but if we’re going to do it it should be the other way around: `Collection as T` with the alias after the name of the protocol.

I like this, it flows better. "Protocol as T where Protocol.Foo == Int, Protocol.Bar : Baz”.

Why did you introduce an alias here and then not use it? Did you mean "Protocol as T where T.Foo == Int, T.Bar : Baz"

Another result of rushing to compose an email. Sorry!

You are also using “dot shorthand” here to refer to an associated type of IntegerLiteralConvertible. I think “dot shorthand” should be limited to cases where there is only one protocol that is getting constrained. In other cases, we need to be clear about which protocol we are referring to.

I borrowed dot shorthand from the generics manifesto. But you are right, it should only be allowed if there is one protocol with associated types or self requirements clause in the Any<...> construction.

I would actually go further and limit it to one protocol period, and possibly even to one protocol and no type names (as types can have nested types and typealiases). When we allow shorthand it should be immediately unambiguous what the shorthand references with no need to look at type or protocol declarations.

It might be desirable to propose the proposal with no allowance for shorthand, and have the dot shorthand be a smaller follow-up proposal.

There will be rules to prevent recursive nesting. For example, if generic typealiases are allowed, they cannot refer to each other in a circular manner (like how structs can't contain themeselves, and you can't create a cyclic graph of enums containing themselves).

How an existential can be used depends on what guarantees are provided by the clauses. For example, 'Any<Equatable>' can't be used for much; if there were any methods on Equatable that did not use the associated types at all you'd be able to call them, but that's about it. However, 'Any<Equatable where .Self == String>' would allow for == to be called on instances. (This is a stupid example, since Any<Equatable where .Self == String> is equivalent to 'String', but there are almost certainly useful examples one could come up with.)

In order of increasing 'power':
Don't constrain any associated types. You can pass around Any<Equatable>s, but that's about it.
Constrain associated types to conform to protocols.
Fully constrain associated types.

I think we need to spell out pretty clearly what members we expect to be available or not available. This section probably needs the most design and elaboration.

For example, we probably can’t access a member who uses an associated type as an input unless it is constrained to a specific type. On the other hand output types probably don’t need to limit access to a member. However, if the output type is Self or an associated type the visible signature would have an output type which has the relevant constraints of the existential applied, but no more. In some cases this means the output type would simply be Any.

Absolutely. This is vaguely what I had in mind but I wanted to get something down first. Thanks for thinking through some of the implications :).

That’s what I thought. Just wanted to start the process of elaborating expectations.

Where this really gets tricky is for compound types like functions, generic types, etc. Working out the details in these cases is pretty complex. I will defer to Doug on whether it is best to just defer those cases to the future, leave them up to the implementer, or try to work out all of the relevant details in the proposal (in which case we probably need a type system expert to help!).

Yes, exactly! For example, can Any<...> existentials involving protocols with associated types or self requirements be used within generic function or type definitions? Maybe there's an argument that existential types of this nature are redundant if you have access to generics (e.g. defining a property on a generic type that is a Collection containing Ints; you should be able to do that today). On the other hand, maybe there are use cases I haven't thought of…

I see no reason they shouldn’t be. They are not redundant at all. For example, you may want to store instances in a heterogeneous collection. You need existentials to do that.

A simple example of what I was referring to there is something like this:

protocol P {
    associatedtype Foo

    func bar(callback: (Foo) -> ())
}

In other words, types in the signature of a protocol member are complex types that reference Self or associated types. I think you really need a formal understanding of the type system to understand how to expose these members through a constrained existential. We can probably understand the expected behavior in some of the simpler cases on a case by case basis, but that approach doesn’t scale at all and is arbitrary. If they’re going to be supported an expert is going to need to be involved in the design.

Yes. I have some ideas regarding this topic.

One area you didn’t touch on is “opening” the existential? Is that out of scope for this proposal? That would be fine with me as this proposal is already taking on a lot. But if so, you should mention something about future directions as it is pretty closely related to this proposal.

Yes, existential opening is explicitly separate from this (although I wanted to mention it in the section where I talk about how Any<Equatable> is not very useful). But you are absolutely right, this proposal should discuss how it wants to interact with possible future directions.

Another area you didn’t touch on is whether Any constructs (and typealiases referring to them) should be usable as generic constraints. I would expect this to be possible but I think we need to spell it out.

I'm hoping for community input. This is a tricky subject, and at some point we'll bump into implementation limitations.

I don’t think it’s too tricky. You can just unpack the constraints of the Any into the list of generic constraints. Maybe I’m missing something, but I don’t think so.

-Matthew

Regards
(From mobile)

I've put together a considerably more detailed draft proposal, taking into account as much of Matthew's feedback as I could. You can find it below:

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

Since there is no chance this will come up for review anytime soon, I expect to make significant revisions to it over the next month or so. Any feedback would be greatly appreciated.

This is very much Swift 4 territory, but I can’t help myself… so…
[…]
I’m not a fan of the “anonymous associated types” terminology: these are associated types of a type of some runtime-defined value. The only thing “anonymous” about them is that it’s harder to spell the base type; otherwise, they’re just like associated types of a generic type parameter. Again, the generics analogy is strong here.

Isn't an 'anonymous associated type' as presented synonymous for 'an existential type used in the position of associated type to another existential type’?

Somewhat. I find it confusing even to talk about it as being “existential”, because it’s not like “x.Index” is any type at run-time: it’s a particular associated type for the runtime type of the existential value “x”. Thinking of it more like a type “T.Index”, where T is effectively a generic parameter describing the runtime type of “x” that cannot really be named directly, makes it fit into a framework I can understand.

It also happens to precisely match the implementation model we’re using for existentials in the compiler, so perhaps I’m biased :)

The section on 'anonymous associated types to real type' would become partially redundant with the general notion of existentials, baring a few possible extra usage limitations.

That’s what I expect, yeah.

the latter of which is fairly important, because it gives nice syntactic sure to one of the most highly-requested features [*]. I’d suggest having that example very, very early.

  - Doug

[*] That generally comes in as “Swift should have parameterized protocols…”

I am unclear about the Metatype section. I was under the impressions that all reflective knownledge of a given type was esposed via Mirrors.

The Metatype section could probably use examples—again, I think it mostly falls out from the analogy with generics. If the protocol has static methods/properties or initializers, those are operations on the metatype.

  - Doug

···

On Jun 5, 2016, at 11:27 PM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:
On Jun 6, 2016, at 1:20 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On May 18, 2016, at 12:35 AM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

Austin

On Tue, May 17, 2016 at 9:52 PM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

On Tue, May 17, 2016 at 1:25 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

Within the angle brackets are zero or more 'clauses'. Clauses are separated by semicolons. (This is so commas can be used in where constraints, below. Better ideas are welcome. Maybe it's not necessary; we can use commas exclusively.)

I’m not a fan of the semicolon idea. I don’t see any reason for this. The `where` keyword separates the protocol list from the constraints just fine. The list on either side should be able to use commas with no problem (or line breaks if that proposal goes through).

I'm leaning towards getting rid of the commas, but would like to write out a few 'dummy' examples to see if there are any readability issues that arise.

Replaced with what? Whitespace separation? I suppose that might work for the protocol list but it feels inconsistent with the rest of Swift. Commas plus (hopefully) the alternative of newline seem like the right direction to me.

Sorry, I completely misspoke (mistyped?). I meant I want to get rid of the semicolons and use commas. I've come to the conclusion that there are no readability issues, protocol<> already uses commas, and semicolons used in this manner don't have a precedent anywhere else in the language.

There are five different possible clauses:

'class'. Must be the first clause, if present. Places a constraint on the existential to be any class type. (Implies: Only one can exist. Mutually exclusive with class name clause.)

(In the future a follow-up proposal should add in 'struct' or 'value' as a counterpart.)

If we’re going to allow `struct` we should also allow `enum`. `value` would allow either of those.

Of course. A future proposal can allow list members to discuss the exact details as to how struct, value, or enum specifiers should work.

Yep, agree. Just mentioning that if we’re going to reference it we should not leave obvious holes in what would be considered. :)

Absolutely.

Class name. Must be the first clause, if present. (Implies: Only one can exist. Mutually exclusive with 'class'.) Places a constraint on the existential (not really an existential anymore) to be an instance of the class, or one of its subclasses.

It is still be an existential if it includes protocol requirements that the class does not fulfill. For example, you might have Any<UIView, SomeProtocol> where UIView does not conform to SomeProtocol, but various subclasses do.

Fair enough. (I don't think the way things work would be affected.)

Your proposal doesn’t discuss composing Any in the way that Adrian’s did like this:

typealias Foo = Any<SomeClass, SomeProtocol, OtherProtocol>
Any<AnotherProtocol, Foo>

I didn't think it needed to be discussed. An Any<...> existential type is a type 'expression' just like any other, and should be allowed to participate in other Any<...>s.

I like the idea of composition as it allows us to factor out constraints. If we are going to do that we should allow a class to be specified in the composition as long is it is a subclass of all class requirements of Any types it composes. For example, this should be allowed:

typealias Bar = Any<SubclassOfSomeClass, Foo, AnotherProtocol>

This is still one class requirement for Bar, it just refines the class requirement of Foo to be SubclassOfSomeClass rather than just SomeClass.

This is a good point. There should be clarification as to how special cases of Any<...> used in another Any<...> behave. For example, like you said Any<MyClass, Any<SomeSubclassOfMyClass, Protocol>> should be valid. This will go into any proposal that emerges from the discussion.

Yes, this is why we need to discuss Any composition. There are also cases of incompatible associated type constraints which need to be rejected (such as composing two Any’s where one has Element == String and another has Element == Int).

Example: Any<UIViewController; UITableViewDataSource; UITableViewDelegate>
"Any UIViewController or subclass which also satisfies the table view data source and delegate protocols"
Dynamic protocol. This is entirely composed of the name of a protocol which has no associated types or Self requirement.
Example: Any<CustomStringConvertible; BooleanType>
"Any type which conforms to both the CustomStringConvertible and BooleanType protocols"

I'm going to use 'static protocol' to refer to a protocol with associated types or self requirements. Feel free to propose a more sound name.

Self-contained static protocol, simple. This is composed of the name of a static protocol, optionally followed by a 'where' clause in which the associated types can be constrained (with any of the three basic conformance types: subclassing, protocol conformance, or type equality). Associated types are referred to with a leading dot.

Please do not introduce terms “dynamic protocol” and “static protocol”. We want to support existentials of protocols that have self or associated type requirements. The dynamic vs static distinction is a limitation of the current implementation of Swift and doesn’t make sense for the long term vision.

I'm not trying to introduce new terms, these are just placeholders. At the same time "protocols with self or associated type requirements" is cumbersome to work with and it would be nice for someone to come up with a descriptive term of art for referring to them.

I agree that a better term would be useful. In the meantime, I would prefer something like “trivial” and “nontrivial” protocols.

I've decided to just use the full name until the community comes up with better names. Clarity is preferable to brevity in this case.

Example: Any<Collection where .Generator.Element : NSObject, .Generator.Element : SomeProtocol>
"Any type that is a Collection, whose elements are NSObjects or their subclasses conforming to SomeProtocol.”

Swift does not allow disjunction of requirements. Only conjunctions are supported. That means the correct reading is:

"Any type that is a Collection, whose elements are NSObjects and their subclasses conforming to SomeProtocol.”

Yes, that is what I meant. "whose elements are (NSObjects or their subclasses) conforming to SomeProtocol”.

Ok, good. Wasn’t quite clear to me.

Yes, the verbiage will need to be clearer in the future. That sentence could be ambiguously parsed.

Bound static protocol. This is the same as a self-contained static protocol, but with a leading "<name> as " which binds the protocol to a generic typealias. The name can be then be used in subsequent clauses to build constraints.

Example: Any<T as Collection; IntegerLiteralConvertible where .IntegerLiteralType == T.Element>.
"Any type that is a Collection, and also can be built from an integer literal, in which the collection elements are the same type as the type of the integer used for the integer literal conformance.”

I’m not sure about this, but if we’re going to do it it should be the other way around: `Collection as T` with the alias after the name of the protocol.

I like this, it flows better. "Protocol as T where Protocol.Foo == Int, Protocol.Bar : Baz”.

Why did you introduce an alias here and then not use it? Did you mean "Protocol as T where T.Foo == Int, T.Bar : Baz"

Another result of rushing to compose an email. Sorry!

You are also using “dot shorthand” here to refer to an associated type of IntegerLiteralConvertible. I think “dot shorthand” should be limited to cases where there is only one protocol that is getting constrained. In other cases, we need to be clear about which protocol we are referring to.

I borrowed dot shorthand from the generics manifesto. But you are right, it should only be allowed if there is one protocol with associated types or self requirements clause in the Any<...> construction.

I would actually go further and limit it to one protocol period, and possibly even to one protocol and no type names (as types can have nested types and typealiases). When we allow shorthand it should be immediately unambiguous what the shorthand references with no need to look at type or protocol declarations.

It might be desirable to propose the proposal with no allowance for shorthand, and have the dot shorthand be a smaller follow-up proposal.

There will be rules to prevent recursive nesting. For example, if generic typealiases are allowed, they cannot refer to each other in a circular manner (like how structs can't contain themeselves, and you can't create a cyclic graph of enums containing themselves).

How an existential can be used depends on what guarantees are provided by the clauses. For example, 'Any<Equatable>' can't be used for much; if there were any methods on Equatable that did not use the associated types at all you'd be able to call them, but that's about it. However, 'Any<Equatable where .Self == String>' would allow for == to be called on instances. (This is a stupid example, since Any<Equatable where .Self == String> is equivalent to 'String', but there are almost certainly useful examples one could come up with.)

In order of increasing 'power':
Don't constrain any associated types. You can pass around Any<Equatable>s, but that's about it.
Constrain associated types to conform to protocols.
Fully constrain associated types.

I think we need to spell out pretty clearly what members we expect to be available or not available. This section probably needs the most design and elaboration.

For example, we probably can’t access a member who uses an associated type as an input unless it is constrained to a specific type. On the other hand output types probably don’t need to limit access to a member. However, if the output type is Self or an associated type the visible signature would have an output type which has the relevant constraints of the existential applied, but no more. In some cases this means the output type would simply be Any.

Absolutely. This is vaguely what I had in mind but I wanted to get something down first. Thanks for thinking through some of the implications :).

That’s what I thought. Just wanted to start the process of elaborating expectations.

Where this really gets tricky is for compound types like functions, generic types, etc. Working out the details in these cases is pretty complex. I will defer to Doug on whether it is best to just defer those cases to the future, leave them up to the implementer, or try to work out all of the relevant details in the proposal (in which case we probably need a type system expert to help!).

Yes, exactly! For example, can Any<...> existentials involving protocols with associated types or self requirements be used within generic function or type definitions? Maybe there's an argument that existential types of this nature are redundant if you have access to generics (e.g. defining a property on a generic type that is a Collection containing Ints; you should be able to do that today). On the other hand, maybe there are use cases I haven't thought of…

I see no reason they shouldn’t be. They are not redundant at all. For example, you may want to store instances in a heterogeneous collection. You need existentials to do that.

A simple example of what I was referring to there is something like this:

protocol P {
    associatedtype Foo

    func bar(callback: (Foo) -> ())
}

In other words, types in the signature of a protocol member are complex types that reference Self or associated types. I think you really need a formal understanding of the type system to understand how to expose these members through a constrained existential. We can probably understand the expected behavior in some of the simpler cases on a case by case basis, but that approach doesn’t scale at all and is arbitrary. If they’re going to be supported an expert is going to need to be involved in the design.

Yes. I have some ideas regarding this topic.

One area you didn’t touch on is “opening” the existential? Is that out of scope for this proposal? That would be fine with me as this proposal is already taking on a lot. But if so, you should mention something about future directions as it is pretty closely related to this proposal.

Yes, existential opening is explicitly separate from this (although I wanted to mention it in the section where I talk about how Any<Equatable> is not very useful). But you are absolutely right, this proposal should discuss how it wants to interact with possible future directions.

Another area you didn’t touch on is whether Any constructs (and typealiases referring to them) should be usable as generic constraints. I would expect this to be possible but I think we need to spell it out.

I'm hoping for community input. This is a tricky subject, and at some point we'll bump into implementation limitations.

I don’t think it’s too tricky. You can just unpack the constraints of the Any into the list of generic constraints. Maybe I’m missing something, but I don’t think so.

-Matthew

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

That does, however, speak for the idea that a concise and obvious syntax
should be supported for that use-case.

Personally, it doesn't seem ridiculous to me that some associated types
might usefully be written as type parameters on a protocol. As
Collection shows, not all associated types are equally important.
Approximately nobody wants the existential “Collection where Index ==
Int.”

···

on Sun Jun 05 2016, Douglas Gregor <swift-evolution@swift.org> wrote:

Sent from my iPhone

On Jun 5, 2016, at 6:41 PM, Matthew Johnson <matthew@anandabits.com> wrote:

Sent from my iPad

On Jun 5, 2016, at 6:20 PM, Douglas Gregor <dgregor@apple.com> wrote:

On May 18, 2016, at 12:35 AM, Austin Zheng <austinzheng@gmail.com> wrote:

I've put together a considerably more detailed draft proposal,
taking into account as much of Matthew's feedback as I could. You
can find it below:

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

Since there is no chance this will come up for review anytime
soon, I expect to make significant revisions to it over the next
month or so. Any feedback would be greatly appreciated.

This is very much Swift 4 territory, but I can’t help myself… so…

The actual feature description is spread out through this very long
document, with user-facing ideas (e.g., using “anonymous associated
types”) intermixed with deeper technical details (existential type
equivalence), so it’s very daunting to read. Please bring the
user-facing features to the front (“Proposed Solution”) with
examples, and save the deeper technical details for “Detailed
Design”. You want more readers to make it through the part that
affects them.

Shortcut 'dot' notation: If there is only one protocol with
associated types specified in the requirements, and there are no
nested Any<...> requirements with where clauses of their own, that
protocol's name can be omitted from the whereclause constraints:

// Okay
// Would otherwise be Any< ~ where Collection.Element == Int>
let a : Any<class, Collection, Any<Streamable, CustomStringConvertible> where .Element == Int>

// NOT ALLOWED
// Both Collection and OptionSetType have associated types.
let b : Any<Collection, OptionSetType where .Element == Int>
FWIW, I think “.Element == Int” should be the only syntax. In
generic signatures, if you have two different protocols with
same-named associated types, and a given type parameter (or
associated type) conforms to both protocols, the associated types
are (implicitly) made equivalent via an inferred same-type
constraint. So there’s no reason to introduce the
“Collection.Element == Int” syntax, because the “Collection” part
is basically irrelevant.

Once existentials have been suitably enhanced, there is a strong
analogy between an existential and a generic signature with a
single type parameter that you can’t name. An existential
Any<Collection where .Element : Equatable> has most of the same
characteristics as a generic something with the signature <T :
Collection where T.Element : Equatable>. Specifically, the sections
on “Existential type equivalence”, “Ordering”, “Real types to
anonymous associated types”, “Anonymous associated types to real
types”. could be reduced to a few small, simple examples and a
mention of the analogous behavior of generics. It will be far
easier to explain this way, and readers don’t need to get immersed
in the details. Where there are differences vs. generics, that’s
important to point out.

“Associated typealias rewriting”: this also falls out of the equivalence with generics + SE-0092.

“Associated types and member exposure”: you don’t make the point
that it only makes sense to refer to the associated types of a let
constant; a var could change its type dynamically, which would
invalidate the typing rules. Did you consider just using
“x.dynamicType” in the type grammar for this? It’s more general, in
that you can refer to associated types but also talk about the
dynamic type of “x” itself, e.g.,

  let x: Equatable = …
  let y: Equatable = …
  if let yAsX = y as? x.dynamicType { … x == yAsX … }

which is (almost?) as powerful as a general “open” expression.

I’m not a fan of the “anonymous associated types” terminology:
these are associated types of a type of some runtime-defined
value. The only thing “anonymous” about them is that it’s harder to
spell the base type; otherwise, they’re just like associated types
of a generic type parameter. Again, the generics analogy is strong
here.

FWIW, I don’t think we’ll ever need “opening existentials” with
what you’ve described here. Also, remember that a method of a
protocol extension essentially opens “Self”, so we already have one
way to open an existential (and that’s probably enough).

I was a little surprised you didn’t point out that AnyObject could become

  typealias AnyObject = Any<class>

or give the nice “AnyCollection” syntax:

  typealias AnyCollection<T> = Any<Collection where .Element == T>

the latter of which is fairly important, because it gives nice
syntactic sure to one of the most highly-requested features
[*]. I’d suggest having that example very, very early.

  - Doug

[*] That generally comes in as “Swift should have parameterized protocols…”

Great feedback here Doug.

FWIW, we also occasionally get "Swift should have parameterized
protocols" in the context of multiple conformances by the same
concrete type (as in things like ConvertibleTo<T> protocol).

I know. From the bugs I've seen it's at least 10x as many requests for
"any collection of some element type" as for any actually reason why
one would need parameterize a protocols.

--
Dave

So without any initial constraints how would one use this generic function??

extension UIButton: ProtocolA {}

let button = UIButton()
let shadowedButton: ProtocolA = UIButton()

// creates a set of a least one element if the generic type could be inferred
func unionIfPossible<T, U>(_ a: T, _ b: U) -> Set<Any<T, U>>? { /* merge somehow if possible */ }

You would not be able to form Any<T, U> here because T and U are generic arguments unknown. We don’t accept those, only `class`, specific classes, and specific protocols (and recursively, other Any).

// this should be valid because the compiler will assume Any<UIView, ProtocolA> where T == UIView and U == ProtocolA
let merged_1: Set<Any<UIView, ProtocolA>> = unionIfPossible( /* UIView subtype */ button, /* ProtocolA */ shadowedButton)

// this won’t be possible because of the restriction
let merged_2: Set<Any<UIView, ProtocolA>> = unionIfPossible(shadowedButton, button)

Any<UIView, ProtocolA> != Any<ProtocolA, UIView> isn’t right. Sure it may feel right for readability but the types should be equal.

"Can be any class type that is a UIView or a subclass of UIView, that also conforms to ProtocolA.“ == "Type that conforms to ProtocolA and that is a UIView or a subclass of UIView.“

This is also a nesting problem where you will be forced to choose the right place inside the angle brackets where to add a nested `Any<…>`.

class A: ClassB, ProtocolA {}

Any<A, Any<ClassB, ProtocolA>> == A != Any<Any<ClassB, ProtocolA>, A> == Any<ClassB, ProtocolA, A> which should be reorder by the compiler and inferred as A

`Any<Any<ClassB, ProtocolA>, A>` is not allowed because if a class is provided it must come first.

`Any<ClassB, ProtocolA, A>` is not allowed because it contains more than one class.

···

On May 18, 2016, at 3:15 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

--
Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 21:23:35, Austin Zheng (austinzheng@gmail.com <mailto:austinzheng@gmail.com>) schrieb:

I strongly prefer keeping the class requirement first, both for readability and because this reflects how protocol and 'class' or superclass conformances already work when defining classes or protocols. I think "look at the first argument, and then recursively look at the first arguments of any nested Any's for class requirements" is straightforward enough not to confuse people, and helps keep the type definitions organized.

Austin

On Wed, May 18, 2016 at 12:11 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On May 18, 2016, at 12:12 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Okay now I feel like we’re merging everything we came up until now :D I’d love to see something like this happen to Swift, because `Any` seems to be a really powerful beast one day.

One quick question: Do we really need this "This must be the first requirement, if present.“?

I’m on the fence about this. The reason it would matter is for readability. The counter argument to that is that you can’t rely on the first argument being a class to determine whether a composed Any will have a class constraint or not.

I’d say the compiler should reorder all types as it wants to. Current protocol<> already doing this today.

e.g.

protocol A {}
protocol B {}

typealias C = protocol<A, B>
typealias D = protocol<B, A>

print(C) // prints protocol<A, B>
print(D) // prints protocol<A, B>
print(C.self == D.self) // prints true

Basically what I mean

Any<SomeProtocol, class, AnotherProtocol>

Any<Any<ProtocolA, ProtocolB>, UIView>

should be valid.

--
Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 18:30:02, Austin Zheng via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

I made heavy revisions to my proposal to reflect all the great feedback I got from you and several other folks (https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md\).

Here are a couple of thoughts:

- I used 'requirements' because that's how the grammar describes similar constructs elsewhere in Swift. Open to change, though.

- Having only one where clause makes complete sense. Protocol extensions and generics all use one where clause, so should this construct.

- The "P can be used in lieu of Any<P>" just means you can declare e.g. "x : Equatable" if you want, instead of "x : Any<Equatable>", just like you can with protocols without associated types or self requirements today.

- I've come to the conclusion that it's probably best to propose the proposal in the most general form, and allow any reviewers on the core team to excise parts they don't think are useful enough or are too difficult to implement. In that spirit, the 'simple Any<...>' construct is gone and Any usage is fully general.

- I moved discussion of typealiases back into the main protocol, like you said, because typealiases using existentials are *not* actually generic and can be done today.

- I added some stuff about 'narrowing' existentials at point of use using as?. This isn't part of 'opening existentials' and should fit in this proposal nicely.

- I want a type expert to look at the 'usage' section, but I'm reasonably sure there's not much more flexibility we can give the user. Parameter associated types can't be treated as covariant (they are almost always invariant or contravariant IIRC) and therefore they should only be accessible if fully bound. Return types can be treated as covariant; some languages do and some don't. (Swift falls into the second bucket.) I would love to be wrong, though.

Austin

On May 18, 2016, at 8:45 AM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

Sent from my iPad

On May 18, 2016, at 2:35 AM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

I've put together a considerably more detailed draft proposal, taking into account as much of Matthew's feedback as I could. You can find it below:

https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md

Since there is no chance this will come up for review anytime soon, I expect to make significant revisions to it over the next month or so. Any feedback would be greatly appreciated.

Thank you for working on this! Great progress.

Minor nit, but I think the proper word is constraint rather than requirement here:

"Within the angle brackets < and > are zero or more requirements. Requirements are separated by commas."

Another tweak:

"P can be used in lieu of Any<P>, where P is a protocol with or without associated type or self requirements."

This proposal is introducing generalized existentials. P and Any<P> should be interchangeable for any protocol regardless of requirements of the protocol. Existentials of protocols with self or associated type requirements that do not include constraints will just expose limited direct functionality. It would still be possible to attempt cast them to concrete types to recover more functionality. In the future (after a follow on proposal) it will also be possible to open the existential.

Thorsten pointed out that there should only be one where clause for the whole existential. This follows the structure of generic type and function constraints. It may also be worth removing the 'as' alias from this proposal. This could be introduced as a stand alone proposal where it would apply to any context with generic constraints.

Another item:
// NOT ALLOWED let a : Any<Any<ProtocolA, ProtocolB>>

Why is this not allowed? It is pointless, but should be allowed and considered identical to the flattened syntax.

On dynamic casting, I don't believe it should be restricted in the way you have defined here. Casting *to* an existential doesn't have anything to do with opening an existential. We should allow casting to any existential type.

On a similar note, I completely disagree with the limitation you specify for use of Any in generic constraints precisely because of your counterargument. In the discussion about moving the where clause it has been noted that sometime it is necessary to apply a lot of constraints to get the necessary effect. A mechanism for factoring constraints is highly desirable and will greatly improve the readability of generic code. Typealiases bound to Any can provide such a mechanism. Let's not artificially restrict the use of it.

The section regarding members of a partly constrained existential needs to be more fleshed out. We can't simply punt it to a future proposal. However, I do think it is a good idea to wait until the core team has time to participate in the discussion.

The section about defining typealias also should not be left to the future. It is possible to define typealias with protocol<> today and to use that alias in a generic constraint. Removing that capability would be a regression. In fact, it's utility will increase significantly with this proposal.

In general, I don't think we need the distinction between simple and full Any. The whole idea of this proposal IMO should be fully generalizing existentials. If restrictions are necessary they should be due to (hopefully temporary) implementation considerations.

Austin

On Tue, May 17, 2016 at 9:52 PM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

On Tue, May 17, 2016 at 1:25 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

Within the angle brackets are zero or more 'clauses'. Clauses are separated by semicolons. (This is so commas can be used in where constraints, below. Better ideas are welcome. Maybe it's not necessary; we can use commas exclusively.)

I’m not a fan of the semicolon idea. I don’t see any reason for this. The `where` keyword separates the protocol list from the constraints just fine. The list on either side should be able to use commas with no problem (or line breaks if that proposal goes through).

I'm leaning towards getting rid of the commas, but would like to write out a few 'dummy' examples to see if there are any readability issues that arise.

Replaced with what? Whitespace separation? I suppose that might work for the protocol list but it feels inconsistent with the rest of Swift. Commas plus (hopefully) the alternative of newline seem like the right direction to me.

Sorry, I completely misspoke (mistyped?). I meant I want to get rid of the semicolons and use commas. I've come to the conclusion that there are no readability issues, protocol<> already uses commas, and semicolons used in this manner don't have a precedent anywhere else in the language.

There are five different possible clauses:

'class'. Must be the first clause, if present. Places a constraint on the existential to be any class type. (Implies: Only one can exist. Mutually exclusive with class name clause.)

(In the future a follow-up proposal should add in 'struct' or 'value' as a counterpart.)

If we’re going to allow `struct` we should also allow `enum`. `value` would allow either of those.

Of course. A future proposal can allow list members to discuss the exact details as to how struct, value, or enum specifiers should work.

Yep, agree. Just mentioning that if we’re going to reference it we should not leave obvious holes in what would be considered. :)

Absolutely.

Class name. Must be the first clause, if present. (Implies: Only one can exist. Mutually exclusive with 'class'.) Places a constraint on the existential (not really an existential anymore) to be an instance of the class, or one of its subclasses.

It is still be an existential if it includes protocol requirements that the class does not fulfill. For example, you might have Any<UIView, SomeProtocol> where UIView does not conform to SomeProtocol, but various subclasses do.

Fair enough. (I don't think the way things work would be affected.)

Your proposal doesn’t discuss composing Any in the way that Adrian’s did like this:

typealias Foo = Any<SomeClass, SomeProtocol, OtherProtocol>
Any<AnotherProtocol, Foo>

I didn't think it needed to be discussed. An Any<...> existential type is a type 'expression' just like any other, and should be allowed to participate in other Any<...>s.

I like the idea of composition as it allows us to factor out constraints. If we are going to do that we should allow a class to be specified in the composition as long is it is a subclass of all class requirements of Any types it composes. For example, this should be allowed:

typealias Bar = Any<SubclassOfSomeClass, Foo, AnotherProtocol>

This is still one class requirement for Bar, it just refines the class requirement of Foo to be SubclassOfSomeClass rather than just SomeClass.

This is a good point. There should be clarification as to how special cases of Any<...> used in another Any<...> behave. For example, like you said Any<MyClass, Any<SomeSubclassOfMyClass, Protocol>> should be valid. This will go into any proposal that emerges from the discussion.

Yes, this is why we need to discuss Any composition. There are also cases of incompatible associated type constraints which need to be rejected (such as composing two Any’s where one has Element == String and another has Element == Int).

Example: Any<UIViewController; UITableViewDataSource; UITableViewDelegate>
"Any UIViewController or subclass which also satisfies the table view data source and delegate protocols"
Dynamic protocol. This is entirely composed of the name of a protocol which has no associated types or Self requirement.
Example: Any<CustomStringConvertible; BooleanType>
"Any type which conforms to both the CustomStringConvertible and BooleanType protocols"

I'm going to use 'static protocol' to refer to a protocol with associated types or self requirements. Feel free to propose a more sound name.

Self-contained static protocol, simple. This is composed of the name of a static protocol, optionally followed by a 'where' clause in which the associated types can be constrained (with any of the three basic conformance types: subclassing, protocol conformance, or type equality). Associated types are referred to with a leading dot.

Please do not introduce terms “dynamic protocol” and “static protocol”. We want to support existentials of protocols that have self or associated type requirements. The dynamic vs static distinction is a limitation of the current implementation of Swift and doesn’t make sense for the long term vision.

I'm not trying to introduce new terms, these are just placeholders. At the same time "protocols with self or associated type requirements" is cumbersome to work with and it would be nice for someone to come up with a descriptive term of art for referring to them.

I agree that a better term would be useful. In the meantime, I would prefer something like “trivial” and “nontrivial” protocols.

I've decided to just use the full name until the community comes up with better names. Clarity is preferable to brevity in this case.

Example: Any<Collection where .Generator.Element : NSObject, .Generator.Element : SomeProtocol>
"Any type that is a Collection, whose elements are NSObjects or their subclasses conforming to SomeProtocol.”

Swift does not allow disjunction of requirements. Only conjunctions are supported. That means the correct reading is:

"Any type that is a Collection, whose elements are NSObjects and their subclasses conforming to SomeProtocol.”

Yes, that is what I meant. "whose elements are (NSObjects or their subclasses) conforming to SomeProtocol”.

Ok, good. Wasn’t quite clear to me.

Yes, the verbiage will need to be clearer in the future. That sentence could be ambiguously parsed.

Bound static protocol. This is the same as a self-contained static protocol, but with a leading "<name> as " which binds the protocol to a generic typealias. The name can be then be used in subsequent clauses to build constraints.

Example: Any<T as Collection; IntegerLiteralConvertible where .IntegerLiteralType == T.Element>.
"Any type that is a Collection, and also can be built from an integer literal, in which the collection elements are the same type as the type of the integer used for the integer literal conformance.”

I’m not sure about this, but if we’re going to do it it should be the other way around: `Collection as T` with the alias after the name of the protocol.

I like this, it flows better. "Protocol as T where Protocol.Foo == Int, Protocol.Bar : Baz”.

Why did you introduce an alias here and then not use it? Did you mean "Protocol as T where T.Foo == Int, T.Bar : Baz"

Another result of rushing to compose an email. Sorry!

You are also using “dot shorthand” here to refer to an associated type of IntegerLiteralConvertible. I think “dot shorthand” should be limited to cases where there is only one protocol that is getting constrained. In other cases, we need to be clear about which protocol we are referring to.

I borrowed dot shorthand from the generics manifesto. But you are right, it should only be allowed if there is one protocol with associated types or self requirements clause in the Any<...> construction.

I would actually go further and limit it to one protocol period, and possibly even to one protocol and no type names (as types can have nested types and typealiases). When we allow shorthand it should be immediately unambiguous what the shorthand references with no need to look at type or protocol declarations.

It might be desirable to propose the proposal with no allowance for shorthand, and have the dot shorthand be a smaller follow-up proposal.

There will be rules to prevent recursive nesting. For example, if generic typealiases are allowed, they cannot refer to each other in a circular manner (like how structs can't contain themeselves, and you can't create a cyclic graph of enums containing themselves).

How an existential can be used depends on what guarantees are provided by the clauses. For example, 'Any<Equatable>' can't be used for much; if there were any methods on Equatable that did not use the associated types at all you'd be able to call them, but that's about it. However, 'Any<Equatable where .Self == String>' would allow for == to be called on instances. (This is a stupid example, since Any<Equatable where .Self == String> is equivalent to 'String', but there are almost certainly useful examples one could come up with.)

In order of increasing 'power':
Don't constrain any associated types. You can pass around Any<Equatable>s, but that's about it.
Constrain associated types to conform to protocols.
Fully constrain associated types.

I think we need to spell out pretty clearly what members we expect to be available or not available. This section probably needs the most design and elaboration.

For example, we probably can’t access a member who uses an associated type as an input unless it is constrained to a specific type. On the other hand output types probably don’t need to limit access to a member. However, if the output type is Self or an associated type the visible signature would have an output type which has the relevant constraints of the existential applied, but no more. In some cases this means the output type would simply be Any.

Absolutely. This is vaguely what I had in mind but I wanted to get something down first. Thanks for thinking through some of the implications :).

That’s what I thought. Just wanted to start the process of elaborating expectations.

Where this really gets tricky is for compound types like functions, generic types, etc. Working out the details in these cases is pretty complex. I will defer to Doug on whether it is best to just defer those cases to the future, leave them up to the implementer, or try to work out all of the relevant details in the proposal (in which case we probably need a type system expert to help!).

Yes, exactly! For example, can Any<...> existentials involving protocols with associated types or self requirements be used within generic function or type definitions? Maybe there's an argument that existential types of this nature are redundant if you have access to generics (e.g. defining a property on a generic type that is a Collection containing Ints; you should be able to do that today). On the other hand, maybe there are use cases I haven't thought of…

I see no reason they shouldn’t be. They are not redundant at all. For example, you may want to store instances in a heterogeneous collection. You need existentials to do that.

A simple example of what I was referring to there is something like this:

protocol P {
    associatedtype Foo

    func bar(callback: (Foo) -> ())
}

In other words, types in the signature of a protocol member are complex types that reference Self or associated types. I think you really need a formal understanding of the type system to understand how to expose these members through a constrained existential. We can probably understand the expected behavior in some of the simpler cases on a case by case basis, but that approach doesn’t scale at all and is arbitrary. If they’re going to be supported an expert is going to need to be involved in the design.

Yes. I have some ideas regarding this topic.

One area you didn’t touch on is “opening” the existential? Is that out of scope for this proposal? That would be fine with me as this proposal is already taking on a lot. But if so, you should mention something about future directions as it is pretty closely related to this proposal.

Yes, existential opening is explicitly separate from this (although I wanted to mention it in the section where I talk about how Any<Equatable> is not very useful). But you are absolutely right, this proposal should discuss how it wants to interact with possible future directions.

Another area you didn’t touch on is whether Any constructs (and typealiases referring to them) should be usable as generic constraints. I would expect this to be possible but I think we need to spell it out.

I'm hoping for community input. This is a tricky subject, and at some point we'll bump into implementation limitations.

I don’t think it’s too tricky. You can just unpack the constraints of the Any into the list of generic constraints. Maybe I’m missing something, but I don’t think so.

-Matthew

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

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

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

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