> On May 17, 2016, at 1:52 PM, Austin Zheng <[email protected]> wrote:
>
> I put together the skeleton of a proposal detailing enhancements to how
> associated types can be referenced in Swift 3+. It's certainly not ready for
> submission, but I think it gets across my ideas pretty well. Would love to
> gather feedback and especially improvements.
>
> Be unsparing; whatever form this feature takes will profoundly affect how
> Swift developers program for years, so it needs to be done right.
>
> See below:
>
> Proposal
>
> An existential type is defined using the "Any<...>" construct. Existential
> types can be nested.
>
> The empty existential type "Any<>" is typealiased to 'Any’.
Also, Any<SomeProtocol> is equivalent to SomeProtocol.
>
> 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).
>
> 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.
>
> 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.
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 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.
> 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.
>
> 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.”
>
> 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.
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.
>
> 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.
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!).
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.
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.
-Matthew
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution