> Am 26.05.2016 um 17:29 schrieb Matthew Johnson <matt...@anandabits.com>: > >> >> On May 26, 2016, at 10:13 AM, Thorsten Seitz <tseit...@icloud.com >> <mailto:tseit...@icloud.com>> wrote: >> >>> >>> Am 26.05.2016 um 16:59 schrieb Matthew Johnson <matt...@anandabits.com >>> <mailto:matt...@anandabits.com>>: >>> >>>> >>>> On May 26, 2016, at 9:52 AM, Thorsten Seitz <tseit...@icloud.com >>>> <mailto:tseit...@icloud.com>> wrote: >>>> >>>>> >>>>> Am 26.05.2016 um 15:40 schrieb Matthew Johnson <matt...@anandabits.com >>>>> <mailto:matt...@anandabits.com>>: >>>>> >>>>> >>>>> >>>>> Sent from my iPad >>>>> >>>>> On May 26, 2016, at 8:25 AM, Thorsten Seitz <tseit...@icloud.com >>>>> <mailto:tseit...@icloud.com>> wrote: >>>>> >>>>>> Ceylon requires checks whether cases are disjoint, i.e. when one case >>>>>> contains a superclass of another case then this will be a type error >>>>>> „cases are not disjoint“. >>>>>> >>>>>> FWIW: Ceylon requires classes with enumerated subclasses to be abstract. >>>>> >>>>> Interesting, thanks for mentioning this. The abstract requirement is >>>>> what makes disjointedness at least partly possible (what if a subclass >>>>> has further descendants though?). But it still only works for a single >>>>> level of inheritance: >>>>> >>>>> sealed abstract class A {} >>>>> class B : A {} >>>>> class C : A {} >>>>> class D : B {} >>>>> class E : B {} >>>>> >>>>> With a disjoint requirement I cannot ever match D and E because that >>>>> would not be exhaustive and I am prohibited from matching them along side >>>>> B which would be exhaustive but isn't disjoint. >>>> >>>> Why? >>>> >>>> switch a { >>>> case C: … >>>> case D: … >>>> case E: … >>>> } >>>> >>>> is exhaustive because B has to be sealed as well (I would require this to >>>> be declared explicitly). >>>> Abstractness seems not to be necessary for that IMO. >>>> Either you match against B *or* against all its subclasses. >>> >>> In my example `B` is not abstract. `a` might have dynamic type of `B` >>> which would not match any of those cases. >> >> Ah, now I realize why the requirement for abstractness makes sense. Of >> course. Thanks for pointing that out! >> >> >>> You didn’t mention that you have to make all non-leaf classes `abstract` >>> (or at least I didn’t understand that from what you wrote). >> >> The requirement in Ceylon is for classes with enumerated subclasses. B would >> have to declare D and E as enumerated subclasses (like I did for Child2 in >> the Ceylon example). In Ceylon this is not required, B could have normal >> subclasses D and E, but then you could not match against D or E at all >> because these would not be exhaustive (B might have other subclasses). The >> problem therefore is not stemming from disjointness requirements. >> As soon as D and E are declared as disjoint (i.e. if B would be declared >> `sealed`) matching against them would be possible but B would have to be >> abstract. > > In my example B is implicitly sealed because it has a sealed ancestor. If it > weren’t you would be able to violate the sealed property of the ancestor by > inheriting from B. > >> >> If I remove the enumerated subclasses from Child2 in the example I am >> allowed to make Child2 non-abstract. This of course results in the type >> error that the cases Child1 | Grandchild1 | Grandchild2 are not covering >> Parent anymore and I have to change the cases to match Child1 | Child2, >> removing the grandchildren matches because they are not disjoint with Child2. > > Got it. The enumerated subclasses are “partial sealing”. They seal one > level of one branch of the inheritance hierarchy. Interesting. You can get > exhaustive matching without needing to seal the entire hierarchy. I’m not > sure if I like this or not. It is something to consider though, and it is > more flexible. > >> >> >>> I don’t like a design that requires non-leaf classes to be abstract. >>> First, it would require introducing abstract classes into Swift, which is a >>> totally separate conversation and something I am not convinced is a good >>> idea. >> >> Well, I think abstract classes are something that is sorely missing in >> Swift! Very sorely missing… > > I’ll keep my mind open about it. My concern is that people will gravitate to > the familiar where protocols would be a better design. I haven’t yet missed > abstract classes in Swift. > >> But as you say that is a separate conversation and an easy fix would be to >> restrict `sealed` to protocols first. > > Why do that? We can have exhaustive switches without requiring abstract.
That’s right. So, exhaustiveness in case of non-abstract B would be given by matching either for B or for exactly B and all its subclasses, i.e. Either switch a { case is C: ... case is B: ... } or switch a { case is C: … case isexactly B: … case is D: … case is E: … } In case of abstract B (in case this would be added to Swift one day) we would have: switch a { case is C: … case is D: … case is E: … } > >> >> >>> But it is also overly restrictive. There are valid cases where you might >>> want an exhaustive switch for a sealed hierarchy that has concrete parent >>> classes. >> >> In that case your suggestion of `isExactly` (or something shorter :-) would >> indeed be the solution. > > Yep. Any ideas for something more concise? Maybe `iss` for „is strictly“? Probably too cryptic, though. -Thorsten > >> >> -Thorsten >> >> >> >>> >>> If you want all non-leaf types to be abstract you should probably consider >>> using protocols in Swift. >>> >>>> >>>> Example in Ceylon: >>>> abstract class Parent() of Child1 | Child2 {} >>>> >>>> class Child1() extends Parent() {} >>>> >>>> abstract class Child2() of Grandchild1 | Grandchild2 extends Parent() {} >>>> >>>> class Grandchild1() extends Child2() {} >>>> >>>> class Grandchild2() extends Child2() {} >>>> >>>> void main() { >>>> Parent foo = Child1(); >>>> >>>> switch (foo) >>>> case (is Child1) { >>>> print("Child1"); >>>> } >>>> case (is Grandchild1) { >>>> print("Grandchild1"); >>>> } >>>> case (is Grandchild2) { >>>> print("Grandchild2"); >>>> } >>>> } >>>> >>>> -Thorsten >>>> >>>> >>>>> >>>>> I don't think that solution is appropriate to Swift. >>>>> >>>>>> >>>>>> -Thorsten >>>>>> >>>>>> >>>>>>> Am 25.05.2016 um 19:49 schrieb Matthew Johnson via swift-evolution >>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>: >>>>>>> >>>>>>> >>>>>>> >>>>>>> Sent from my iPad >>>>>>> >>>>>>> On May 25, 2016, at 12:41 PM, Charlie Monroe <char...@charliemonroe.net >>>>>>> <mailto:char...@charliemonroe.net>> wrote: >>>>>>> >>>>>>>>> Got it. You could also say it is safer because you can't have a >>>>>>>>> supertype case "swallow" a subtype value accidentally. An "exact >>>>>>>>> type" cast would prevent this possibility. >>>>>>>> >>>>>>>> This still can be an issue since you still need to do the switch in >>>>>>>> init(instance:), but it's just one place within the entire module, so >>>>>>>> it can be more easily managed... >>>>>>> >>>>>>> Yes, agree. That's why your enum is safer. I think we do need an >>>>>>> exact type cast to prevent this problem. 'isExaclty' and 'asExactly' >>>>>>> seem are a bit verbose but are very clear. I can't think of anything I >>>>>>> like that is more concise. >>>>>>> >>>>>>>> >>>>>>>>> >>>>>>>>>> >>>>>>>>>> enum AnimalSubclasses { >>>>>>>>>> >>>>>>>>>> case Dog >>>>>>>>>> case Cat >>>>>>>>>> >>>>>>>>>> init(instance: Animal) { >>>>>>>>>> switch instance { >>>>>>>>>> case is Dog: self = .Dog >>>>>>>>>> case is Cat: self = .Cat >>>>>>>>>> default: fatalError("Unhandled instance \(instance)!") >>>>>>>>>> } >>>>>>>>>> >>>>>>>>>> } >>>>>>>>>> >>>>>>>>>>> One thing I have considered that might also be worth introducing is >>>>>>>>>>> an exact match cast. This would prevent the possibility of putting >>>>>>>>>>> a superclass case first and having it “steal” subclasses which were >>>>>>>>>>> intended to be covered by a case later in the switch. If we >>>>>>>>>>> introduce exact match you would be able to write a switch that must >>>>>>>>>>> always cover every concrete type, including all subclasses. >>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>> Charlie >>>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>>> On May 25, 2016, at 4:41 AM, Leonardo Pessoa via swift-evolution >>>>>>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> >>>>>>>>>>>>> wrote: >>>>>>>>>>>>> >>>>>>>>>>>>> Limiting the amount of subclasses is not really a good idea as >>>>>>>>>>>>> you would need to introduce another mechanism in the language >>>>>>>>>>>>> while the proposed feature requires much less. And you're >>>>>>>>>>>>> thinking only about the restrictive set (internal and private) >>>>>>>>>>>>> and forgetting the more open end (public). Why is it so bad for >>>>>>>>>>>>> this proposal to support requiring the default case? If its >>>>>>>>>>>>> possible for the compiler to discover you covered all possible >>>>>>>>>>>>> cases it would be fine not having default but IMHO in most cases >>>>>>>>>>>>> it will find out there are more not explicitly covered. >>>>>>>>>>>>> From: David Sweeris <mailto:daveswee...@mac.com> >>>>>>>>>>>>> Sent: 24/05/2016 11:01 PM >>>>>>>>>>>>> To: Austin Zheng <mailto:austinzh...@gmail.com> >>>>>>>>>>>>> Cc: Leonardo Pessoa <mailto:m...@lmpessoa.com>; swift-evolution >>>>>>>>>>>>> <mailto:swift-evolution@swift.org> >>>>>>>>>>>>> Subject: Re: [swift-evolution] [Pitch] Exhaustive pattern >>>>>>>>>>>>> matching forprotocols and classes >>>>>>>>>>>>> >>>>>>>>>>>>> Or if there was a way to declare that a class/protocol can only >>>>>>>>>>>>> have a defined set of subclasses/conforming types. >>>>>>>>>>>>> >>>>>>>>>>>>> Sent from my iPhone >>>>>>>>>>>>> >>>>>>>>>>>>> On May 24, 2016, at 15:35, Austin Zheng via swift-evolution >>>>>>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> >>>>>>>>>>>>> wrote: >>>>>>>>>>>>> >>>>>>>>>>>>>> If you pattern match on a type that is declared internal or >>>>>>>>>>>>>> private, it is impossible for the compiler to not have an >>>>>>>>>>>>>> exhaustive list of subclasses that it can check against. >>>>>>>>>>>>>> >>>>>>>>>>>>>> Austin >>>>>>>>>>>>>> >>>>>>>>>>>>>> On Tue, May 24, 2016 at 1:29 PM, Leonardo Pessoa >>>>>>>>>>>>>> <m...@lmpessoa.com <mailto:m...@lmpessoa.com>> wrote: >>>>>>>>>>>>>> I like this but I think it would be a lot hard to ensure you >>>>>>>>>>>>>> have all >>>>>>>>>>>>>> subclasses covered. Think of frameworks that could provide many >>>>>>>>>>>>>> unsealed classes. You could also have an object that would have >>>>>>>>>>>>>> to >>>>>>>>>>>>>> handle a large subtree (NSObject?) and the order in which the >>>>>>>>>>>>>> cases >>>>>>>>>>>>>> are evaluated would matter just as in exception handling in >>>>>>>>>>>>>> languages >>>>>>>>>>>>>> such as Java (or require some evaluation from the compiler to >>>>>>>>>>>>>> raise >>>>>>>>>>>>>> warnings). I'm +1 for this but these should be open-ended like >>>>>>>>>>>>>> strings >>>>>>>>>>>>>> and require the default case. >>>>>>>>>>>>>> >>>>>>>>>>>>>> On 24 May 2016 at 17:08, Austin Zheng via swift-evolution >>>>>>>>>>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> >>>>>>>>>>>>>> wrote: >>>>>>>>>>>>>> > I have been hoping for the exhaustive pattern matching feature >>>>>>>>>>>>>> > for a while >>>>>>>>>>>>>> > now, and would love to see a proposal. >>>>>>>>>>>>>> > >>>>>>>>>>>>>> > Austin >>>>>>>>>>>>>> > >>>>>>>>>>>>>> > On Tue, May 24, 2016 at 1:01 PM, Matthew Johnson via >>>>>>>>>>>>>> > swift-evolution >>>>>>>>>>>>>> > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> >>>>>>>>>>>>>> > wrote: >>>>>>>>>>>>>> >> >>>>>>>>>>>>>> >> Swift currently requires a default pattern matching clause >>>>>>>>>>>>>> >> when you switch >>>>>>>>>>>>>> >> on an existential or a non-final class even if the protocol >>>>>>>>>>>>>> >> or class is >>>>>>>>>>>>>> >> non-public and all cases are covered. It would be really >>>>>>>>>>>>>> >> nice if the >>>>>>>>>>>>>> >> default clause were not necessary in this case. The compiler >>>>>>>>>>>>>> >> has the >>>>>>>>>>>>>> >> necessary information to prove exhaustiveness. >>>>>>>>>>>>>> >> >>>>>>>>>>>>>> >> Related to this is the idea of introducing something like a >>>>>>>>>>>>>> >> `sealed` >>>>>>>>>>>>>> >> modifier that could be applied to public protocols and >>>>>>>>>>>>>> >> classes. The >>>>>>>>>>>>>> >> protocol or class would be visible when the module is >>>>>>>>>>>>>> >> imported, but >>>>>>>>>>>>>> >> conformances or subclasses outside the declaring module would >>>>>>>>>>>>>> >> be prohibited. >>>>>>>>>>>>>> >> Internal and private protocols and classes would implicitly >>>>>>>>>>>>>> >> be sealed since >>>>>>>>>>>>>> >> they are not visible outside the module. Any protocols that >>>>>>>>>>>>>> >> inherit from a >>>>>>>>>>>>>> >> sealed protocol or classes that inherit from a sealed class >>>>>>>>>>>>>> >> would also be >>>>>>>>>>>>>> >> implicitly sealed (if we didn’t do this the sealing of the >>>>>>>>>>>>>> >> superprotocol / >>>>>>>>>>>>>> >> superclass could be violated by conforming to or inheriting >>>>>>>>>>>>>> >> from a >>>>>>>>>>>>>> >> subprotocol / subclass). >>>>>>>>>>>>>> >> >>>>>>>>>>>>>> >> Here are examples that I would like to see be valid: >>>>>>>>>>>>>> >> >>>>>>>>>>>>>> >> protocol P {} >>>>>>>>>>>>>> >> // alternatively public sealed protocol P {} >>>>>>>>>>>>>> >> struct P1: P {} >>>>>>>>>>>>>> >> struct P2: P {} >>>>>>>>>>>>>> >> >>>>>>>>>>>>>> >> func p(p: P) -> Int { >>>>>>>>>>>>>> >> switch p { >>>>>>>>>>>>>> >> case is P1: return 1 // alternatively an `as` cast >>>>>>>>>>>>>> >> case is P2: return 2 // alternatively an `as` cast >>>>>>>>>>>>>> >> } >>>>>>>>>>>>>> >> } >>>>>>>>>>>>>> >> >>>>>>>>>>>>>> >> class C {} >>>>>>>>>>>>>> >> // alternatively public sealed class C {} >>>>>>>>>>>>>> >> class C1: C {} >>>>>>>>>>>>>> >> class C2: C {} >>>>>>>>>>>>>> >> >>>>>>>>>>>>>> >> func c(c: C) -> Int { >>>>>>>>>>>>>> >> switch c { >>>>>>>>>>>>>> >> case is C1: return 1 // alternatively an `as` cast >>>>>>>>>>>>>> >> case is C2: return 2 // alternatively an `as` cast >>>>>>>>>>>>>> >> case is C: return 0 // alternatively an `as` cast >>>>>>>>>>>>>> >> } >>>>>>>>>>>>>> >> } >>>>>>>>>>>>>> >> >>>>>>>>>>>>>> >> I am wondering if this is something the community is >>>>>>>>>>>>>> >> interested in. If >>>>>>>>>>>>>> >> so, I am wondering if this is something that might be >>>>>>>>>>>>>> >> possible in the Swift >>>>>>>>>>>>>> >> 3 timeframe (maybe just for private and internal protocols >>>>>>>>>>>>>> >> and classes) or >>>>>>>>>>>>>> >> if it should wait for Swift 4 (this is likely the case). >>>>>>>>>>>>>> >> >>>>>>>>>>>>>> >> -Matthew >>>>>>>>>>>>>> >> _______________________________________________ >>>>>>>>>>>>>> >> swift-evolution mailing list >>>>>>>>>>>>>> >> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>>>>>>>>>>> >> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>>>>>>>>>> >> <https://lists.swift.org/mailman/listinfo/swift-evolution> >>>>>>>>>>>>>> > >>>>>>>>>>>>>> > >>>>>>>>>>>>>> > >>>>>>>>>>>>>> > _______________________________________________ >>>>>>>>>>>>>> > swift-evolution mailing list >>>>>>>>>>>>>> > swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>>>>>>>>>>> > https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>>>>>>>>>> > <https://lists.swift.org/mailman/listinfo/swift-evolution> >>>>>>>>>>>>>> > >>>>>>>>>>>>>> >>>>>>>>>>>>>> _______________________________________________ >>>>>>>>>>>>>> swift-evolution mailing list >>>>>>>>>>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>>>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>>>>>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution> >>>>>>>>>>>>> _______________________________________________ >>>>>>>>>>>>> swift-evolution mailing list >>>>>>>>>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>>>>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution> >>>>>>>>>>>> >>>>>>>>>>>> _______________________________________________ >>>>>>>>>>>> swift-evolution mailing list >>>>>>>>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>>>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution> >>>>>>>> >>>>>>> _______________________________________________ >>>>>>> swift-evolution mailing list >>>>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org> >>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution