On Sun, Jan 29, 2017 at 4:01 PM, David Hart via swift-evolution < swift-evolution@swift.org> 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 <matt...@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? > > 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. > > That's a _reasonable_ set of answers if you want to require Base to precede Protocol *and* you want to ease rules for typealiases. However, using your notation, should `(Protocol & Protocol) & (Base & Protocol)` be allowed? If not, your rules will have to get pretty complicated. OTOH, if so, it seems like an awfully heavy-handed yet simultaneously ineffective hardcoding of a style preference, since I'd be able to use `typealias Base_ = Base & Any` to circumvent the rule any time I like. 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 <da...@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- > existentials/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@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