> On 29 Jan 2017, at 23:12, Xiaodi Wu <[email protected]> wrote: > >> On Sun, Jan 29, 2017 at 4:01 PM, David Hart via swift-evolution >> <[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? >> >>> 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 & Protocol3 >> Protocol1 & Something >> Questions >> >> Should class requirements be fixed to first position? I.e., should Protocol >> & Base be valid and equivalent to Base & Protocol? >> Should repetition of class requirements in the same declaration be allowed? >> I.e., should Base & Base be valid and equivalent to Base? >> Should repetition of class requirements through typealias expansion be >> allowed? I.e., should Base & (Base & Protocol) be valid and equivalent to >> Base & Protocol? >> 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? >> 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 >> >> No, for the reasons stated above. >> No, because it doesn’t make sense to repeat it in the same declaration. >> Yes, I’m gonna start agreeing with you and think will ease typealias >> composition. >> No, for the same reasons as 2. >> 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?
Yes, it would be allowed. I'll mention this example when I write the proposals second draft. Thanks! > 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 <[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-existentials/proposals/XXXX-subclass-existentials.md >>>> >>>> Regards, >>>> David. >>>> >>>> Existentials for classes conforming to protocols >>>> Proposal: SE-XXXX >>>> Authors: David Hart, Austin Zheng >>>> Review Manager: TBD >>>> Status: TBD >>>> 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. >>>> >>>> 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. >>>> >>>> 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 before >>>> >>>> let b: MyObject & Hashable >>>> // VALID: This is the new rule which allows an object type in first >>>> position >>>> >>>> let c: CustomStringConvertible & MyObject >>>> // INVALID: MyObject is not allowed in second position. A fix-it should >>>> help transform it to: >>>> // let c: MyObject & CustomStringConvertible >>>> >>>> typealias MyObjectStringConvertible = MyObject & CustomStringConvertible >>>> let 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 & Hashable >>>> >>>> typealias MyObjectStringConvertible = MyObject & CustomStringConvertible >>>> let 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 & CustomStringConvertible >>>> >>>> let b: MyObjectSubclass & MyObject & Hashable >>>> // This is equivalent to MyObjectSubclass & Hashable >>>> >>>> typealias MyObjectStringConvertible = MyObject & CustomStringConvertible >>>> let d: MyObject & MyObjectStringConvertible >>>> // This is equivalent to MyObject & CustomStringConvertible >>>> 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. >>>> Alternatives considered >>>> >>>> None. >>>> >>>> Acknowledgements >>>> >>>> Thanks to Austin Zheng and Matthew Johnson 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
