On Sun, Jan 29, 2017 at 6:00 PM, Matthew Johnson <[email protected]> wrote:
> > On Jan 29, 2017, at 4:48 PM, Xiaodi Wu <[email protected]> wrote: > > On Sun, Jan 29, 2017 at 4:19 PM, Matthew Johnson via swift-evolution < > [email protected]> wrote: > >> >> On Jan 29, 2017, at 4:01 PM, David Hart <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 >> [email protected] >> https://lists.swift.org/mailman/listinfo/swift-evolution >> >> > >
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
