[swift-evolution] Subclass Existentials

Xiaodi Wu xiaodi.wu at gmail.com
Sun Jan 29 18:48:05 CST 2017


On Sun, Jan 29, 2017 at 6:00 PM, Matthew Johnson <matthew at anandabits.com>
wrote:

>
> On Jan 29, 2017, at 4:48 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
> On Sun, Jan 29, 2017 at 4:19 PM, Matthew Johnson via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>>
>> On Jan 29, 2017, at 4:01 PM, David Hart <david at hartbit.com> wrote:
>>
>> Hi Matthew,
>>
>> I’ll reply to this post, because it allows me to discuss a few of the
>> points in the discussion, but I’ve read the whole discussion.
>>
>> On 29 Jan 2017, at 18:30, Matthew Johnson <matthew at anandabits.com> wrote:
>>
>> Hi David,
>>
>> This looks like a great start.
>>
>> One thing we should consider is whether we tie this proposal so tightly
>> to classes or whether it might be better to call these supertype
>> constraints.  The feature might also be useful for value types if / when
>> Swift gets value subtyping.
>>
>>
>> This makes sense, especially with the Substring : String discussions
>> going on. When I rework the proposal, I’ll try to make it more general.
>>
>> One enhancement that might be worth considering.  Specifically, allowing
>> protocols to declare a specific supertype requirement in the place where a
>> `class` constraint would usually be found.  After this proposal we could
>> already do something similar using a `class` constraint to define a
>> protocol and a typealias to bind it to the supertype requirement.  It seems
>> like allowing users to state this more directly would be a good idea.
>>
>>
>> You lost me there. Can you give examples?
>>
>>
>> Sure.  In an iOS app you might want to create a protocol that should only
>> be conformed to by `UIViewController` subclasses:
>>
>> protocol TrackedViewController: UIViewController {
>>     var analyticsTitle: String { get }
>> }
>>
>> This is roughly equivalent to under your proposal:
>>
>> protocol TrackedObject: class {
>>     var analyticsTitle: String { get }
>> }
>> typealias TrackedViewController = UIViewController & TrackedObject
>>
>> But in this case we would be able to avoid the TrackedObject protocol
>> which we didn’t care about or want, but were required to create in the
>> process of creating our typealias.
>>
>
> Since we allow only one subclass, this gets a little tricky, right? If
> TrackedViewController refines UIViewController, then should it count as a
> subclass requirement when it comes to further composition and be required
> first?
>
>
> It would be treated the same as the typealias as far as the syntax of
> composing existential types with `&` is concerned.  I imagine the sensible
> thing, if we have the “class comes first requirement" is:
>
> protocol P {}
> typealias Foo = TrackedViewController & P // allowed
> typealias Foo = P & TrackedViewController // not allowed
>
> class MyViewController: UIVIewController, TrackedViewController {}
>
>
>
> The syntax also implies that everything on UIViewController is a
> requirement for a TrackedViewController. Would a class that conforms to
> TrackedViewController automatically be a subclass of UIViewController, or
> would only subclasses of UIViewController be allowed to conform to
> TrackedViewController (i.e. is _being_ a UIViewController a requirement for
> conforming to TrackedViewController)? The former seems surprising, at least
> to me. Yet in the latter case, this would behave very differently from
> protocols you put on the right side of the colon.
>
>
> I hadn’t considered this and it’s a good question.  Conforming classes
> should still have to declare their immediate superclass.  What it adds is a
> requirement that conforming types *eventually* have the constraining type
> as a supertype.  A class can pick up implementation of other requirements
> from it’s superclass so why shouldn’t it’s superclass also be able to meet
> a supertype requirement.
>
> Would it make sense to allow a protocol to refine a struct, too?
>
>
> Eventually, yes.  Immediately, maybe.  I think this makes sense as soon as
> we get value subtyping and / or we adopt the intersection types view and
> allow uninhabited existentials.  Until then we probably don’t gain anything
> from it.
>
> Adding it immediately would reduce the need to make a future change and /
> or remember to add it when we add the other features and it probably
> wouldn’t cause any significant issues for users.  On the other hand, it’s
> easy to make a case that adding it now is unnecessary and speculative
> complexity and we should wait until we have a real use for it.
>
>
> IMO this idea might be best considered separately. Could be nice to have,
> but it touches on a little more than existentials.
>
>
> Are we going to be allowed to use these existential types as generic
> constraints?  That was allowed in Austin’s proposal.  If we are going to
> support this it makes sense to me that we should have support for supertype
> requirements in protocols.  After all, what is the role of a concrete type
> name in the existential syntax other than to introduce what is effectively
> an anonymous protocol with a supertype requirement?
>

I didn't think that supporting existential types as generic constraints was
part of _this_ proposal? It'd be nice to have that, but IMO it's a separate
discussion.

As only the first element in the existential composition syntax can be a
>> class type, and by extending this rule to typealias expansions, we can make
>> sure that we only need to read the first element to know if it contains a
>> class requirement.
>>
>> I think this is unnecessarily limiting in a couple of ways.  I agree that
>> a class should come first if it is mentioned explicitly***.  I am less sure
>> we should require this when the type is part of a typealias combined with
>> other protocol requirements.
>>
>>
>> I agree with Chris that I think it’s important to require the class be
>> mentioned first when the class is mentioned explicitly. Even if we lost the
>> Any<Base, Protocol> syntax, there is still enough similarity to a class’s
>> inheritance/conformance clause to keep the consistency and readability.
>>
>> For example, one use case I remember discussing with Austin is refining
>> supertype requirements.  If I have a typealias which requires a superclass
>> `Base` I should be able to form an existential using that typealias that
>> *refines* that requirement to some type *Derived* which is a non-final
>> subtype of `Base`.  This would require syntax that allows us to put a class
>> name in the first position, but also mention a typealias with a supertype
>> requirement in a subsequent position.
>>
>>
>> I’ve read the examples in the thread and I think I agree that those cases
>> should be accepted. But just to make sure we are on the same page, what
>> does everyone think of the validity of the following cases? For shorthand,
>> I use parentheses to represent typealias expansion. For example, when I
>> write:
>>
>> Protocol1 & (Protocol2 & Protocol3)
>>
>> I mean:
>>
>> typealias Something = Protocol2 & Protocol3Protocol1 & Something
>>
>> *Questions*
>>
>>
>>    1. Should class requirements be fixed to first position? I.e., should Protocol
>>    & Base be valid and equivalent to Base & Protocol?
>>    2. Should repetition of class requirements in the same declaration be
>>    allowed? I.e., should Base & Base be valid and equivalent to Base?
>>    3. Should repetition of class requirements through typealias
>>    expansion be allowed? I.e., should Base & (Base & Protocol) be valid
>>    and equivalent to Base & Protocol?
>>    4. Should type and sub-type requirements in the same declaration be
>>    allowed? I.e., should Base & Derived or Derived & Base be valid and
>>    equivalent to Derived?
>>    5. Should type and sub-type requirements through typealias expansion
>>    be allowed? I.e., should Base & (Derived & Protocol) or Derived &
>>    (Base & Protocol) be valid and equivalent to Derived & Protocol?
>>
>> *My Answers*
>>
>>
>>    1. No, for the reasons stated above.
>>    2. No, because it doesn’t make sense to repeat it in the same
>>    declaration.
>>    3. Yes, I’m gonna start agreeing with you and think will ease
>>    typealias composition.
>>    4. No, for the same reasons as 2.
>>    5. Yes, for the same reasons as 3.
>>
>> My answer depends on whether we adopt the perspective of intersection
>> types and allow things like `Base1 & Base2` or not.  If we’re not going to
>> do that (at least not yet) I think your answers are the right ones.
>>
>> However, if we decide to allow uninhabiatable existentials like`Base1 &
>> Base2` I think it would be best to just relax the rules such that they
>> follow the logic of intersection types.
>>
>> The good news is that (as far as I can tell) it would be an additive
>> change to start with your rules and later adopt the intersection types
>> approach.
>>
>>
>> David.
>>
>> Matthew
>>
>> *** One argument against requiring a class to come first is that we could
>> conceptualize `&` as a type operator with a handful of overloads.  This
>> would include both lhs and rhs are “protocol only kinds” as well as
>> overloads with a “protocol only kind” in either lhs or rhs and a “supertype
>> kind” in the other position.  The tricky part of pulling this off would be
>> including an overload where both lhs and rhs have a “supertype kind”, but
>> only when the operands have a subtype / supertype relationship with each
>> other.
>>
>> I suspect this conceptualization isn’t worth the complexity it brings,
>> but it is tempting to try and view `&` as a type operator.  As long as this
>> only involves a loosening of restrictions it could probably be introduced
>> as an additive change down the road.
>>
>> On Jan 29, 2017, at 10:39 AM, David Hart <david at hartbit.com> wrote:
>>
>> Hello,
>>
>> As promised, I wrote the first draft of a proposal to add class
>> requirements to the existential syntax. Please let me know what you think.
>>
>> https://github.com/hartbit/swift-evolution/blob/subclass-exi
>> stentials/proposals/XXXX-subclass-existentials.md
>>
>> Regards,
>> David.
>>
>> Existentials for classes conforming to protocols
>>
>>    - Proposal: SE-XXXX
>>    <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
>>    - Authors: David Hart <http://github.com/hartbit/>, Austin Zheng
>>    <http://github.com/austinzheng>
>>    - Review Manager: TBD
>>    - Status: TBD
>>
>>
>> <https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>
>> Introduction
>>
>> This proposal brings more expressive power to the type system by allowing
>> Swift to represent existentials of classes and subclasses which conform to
>> protocols.
>>
>> <https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>
>> Motivation
>>
>> Currently, the only existentials which can be represented in Swift are
>> conformances to a set of protocols, using the &syntax:
>>
>> let existential: Hashable & CustomStringConvertible
>>
>> On the other hand, Objective-C is capable of expressing existentials of
>> subclasses conforming to protocols with the following syntax:
>>
>> UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
>>
>> We propose to provide similar expressive power to Swift, which will also
>> improve the bridging of those types from Objective-C.
>>
>> <https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed
>> solution
>>
>> The proposal keeps the existing & syntax but allows the first element,
>> and only the first, to be of class type. The equivalent declaration to the
>> above Objective-C declaration would look like this:
>>
>> let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
>>
>> As in Objective-C, this existential represents classes which have
>> UIViewController in their parent inheritance hierarchy and which also
>> conform to the UITableViewDataSource and UITableViewDelegate protocols.
>>
>> As only the first element in the existential composition syntax can be a
>> class type, and by extending this rule to typealias expansions, we can make
>> sure that we only need to read the first element to know if it contains a
>> class requirement. As a consequence, here is a list of valid and invalid
>> code and the reasons for them:
>>
>> let a: Hashable & CustomStringConvertible// VALID: This is still valid, as beforelet b: MyObject & Hashable// VALID: This is the new rule which allows an object type in first positionlet c: CustomStringConvertible & MyObject// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:// let c: MyObject & CustomStringConvertibletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet d: Hashable & MyObjectStringConvertible// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:// let d: MyObjectStringConvertible & Hashabletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet e: MyOtherObject & MyObjectStringConvertible// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
>>
>> The following examples could technically be legal, but we believe we
>> should keep them invalid to keep the rules simple:
>>
>> let a: MyObject & MyObject & CustomStringConvertible// This is equivalent to MyObject & CustomStringConvertiblelet b: MyObjectSubclass & MyObject & Hashable// This is equivalent to MyObjectSubclass & Hashabletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet d: MyObject & MyObjectStringConvertible// This is equivalent to MyObject & CustomStringConvertible
>>
>>
>> <https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source
>> compatibility
>>
>> This is a source breaking change. All types bridged from Objective-C
>> which use the equivalent Objective-C feature import without the protocol
>> conformances in Swift 3. This change would increase the existential's
>> requirement and break on code which does not meet the new protocol
>> requirements. For example, the following Objective-C code:
>>
>> @interface MyViewController
>> - (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;@end
>>
>> is imported into Swift 3 as:
>>
>> class MyViewController {
>>     func setup(tableViewController: UIViewController) {}
>> }
>>
>> which allows calling the function with an invalid parameter:
>>
>> let myViewController: MyViewController()
>> myViewController.setup(UIViewController())
>>
>> The previous code would have worked as long as the Objective-C code did
>> not call any method of UITableViewDataSource or UITableViewDelegate. But
>> if this proposal is accepted and implemented as-is, the Objective-C code
>> would now be imported as:
>>
>> class MyViewController {
>>     func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
>> }
>>
>> That would then cause the Swift code to fail to compile with an error
>> which states that UIViewController does not conform to the
>> UITableViewDataSource and UITableViewDelegate protocols.
>>
>> It is a source-breaking change, but should have a minimal impact for the
>> following reasons:
>>
>>    - Not many Objective-C code used the existential syntax in practice.
>>    - There generated errors are a good thing because they point out
>>    potential crashes which would have gone un-noticed.
>>
>>
>> <https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives
>> considered
>>
>> None.
>>
>> <https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>
>> Acknowledgements
>> Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew
>> Johnson <https://github.com/anandabits> who brought a lot of attention
>> to existentials in this mailing-list and from whom most of the ideas in the
>> proposal come from.
>>
>>
>>
>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170129/5f216d8d/attachment.html>


More information about the swift-evolution mailing list