> Am 26.05.2016 um 16:59 schrieb Matthew Johnson <[email protected]>:
>
>>
>> On May 26, 2016, at 9:52 AM, Thorsten Seitz <[email protected]
>> <mailto:[email protected]>> wrote:
>>
>>>
>>> Am 26.05.2016 um 15:40 schrieb Matthew Johnson <[email protected]
>>> <mailto:[email protected]>>:
>>>
>>>
>>>
>>> Sent from my iPad
>>>
>>> On May 26, 2016, at 8:25 AM, Thorsten Seitz <[email protected]
>>> <mailto:[email protected]>> 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.
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.
> 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...
But as you say that is a separate conversation and an easy fix would be to
restrict `sealed` to protocols first.
> 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.
-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
>>>>> <[email protected] <mailto:[email protected]>>:
>>>>>
>>>>>
>>>>>
>>>>> Sent from my iPad
>>>>>
>>>>> On May 25, 2016, at 12:41 PM, Charlie Monroe <[email protected]
>>>>> <mailto:[email protected]>> 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
>>>>>>>>>>> <[email protected] <mailto:[email protected]>>
>>>>>>>>>>> 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:[email protected]>
>>>>>>>>>>> Sent: 24/05/2016 11:01 PM
>>>>>>>>>>> To: Austin Zheng <mailto:[email protected]>
>>>>>>>>>>> Cc: Leonardo Pessoa <mailto:[email protected]>; swift-evolution
>>>>>>>>>>> <mailto:[email protected]>
>>>>>>>>>>> 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
>>>>>>>>>>> <[email protected] <mailto:[email protected]>>
>>>>>>>>>>> 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
>>>>>>>>>>>> <[email protected] <mailto:[email protected]>> 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
>>>>>>>>>>>> <[email protected] <mailto:[email protected]>>
>>>>>>>>>>>> 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
>>>>>>>>>>>> > <[email protected] <mailto:[email protected]>>
>>>>>>>>>>>> > 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
>>>>>>>>>>>> >> [email protected] <mailto:[email protected]>
>>>>>>>>>>>> >> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>>>>>> >> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>>>>>>>> >
>>>>>>>>>>>> >
>>>>>>>>>>>> >
>>>>>>>>>>>> > _______________________________________________
>>>>>>>>>>>> > swift-evolution mailing list
>>>>>>>>>>>> > [email protected] <mailto:[email protected]>
>>>>>>>>>>>> > https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>>>>>> > <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>>>>>>>> >
>>>>>>>>>>>>
>>>>>>>>>>>> _______________________________________________
>>>>>>>>>>>> swift-evolution mailing list
>>>>>>>>>>>> [email protected] <mailto:[email protected]>
>>>>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>>>>>>> _______________________________________________
>>>>>>>>>>> swift-evolution mailing list
>>>>>>>>>>> [email protected] <mailto:[email protected]>
>>>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>>>>>>
>>>>>>>>>> _______________________________________________
>>>>>>>>>> swift-evolution mailing list
>>>>>>>>>> [email protected] <mailto:[email protected]>
>>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>>
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> [email protected] <mailto:[email protected]>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution