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...
···
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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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.
···
Sent from my iPad
On May 25, 2016, at 12:41 PM, Charlie Monroe <charlie@charliemonroe.net> wrote:
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> 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
Sent: 24/05/2016 11:01 PM
To: Austin Zheng
Cc: Leonardo Pessoa; swift-evolution
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> 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 <me@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> 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> 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
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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.
This doesn't at all solve the parent issue of switching exhaustively, but
switching over dynamicType distinguishes subclasses from base classes quite
adequately:
class Foo { }
class Bar : Foo { }
class Baz : Foo { }
let b = Bar()
switch b.dynamicType {
case let t where t == Foo.self:
print("Foo!")
case let t where t == Bar.self:
print("Bar!")
case let t where t == Baz.self:
print("Baz!")
default:
print("Boo...")
}
···
On Wed, May 25, 2016 at 12:49 PM, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:
On May 25, 2016, at 12:41 PM, Charlie Monroe <charlie@charliemonroe.net> > wrote:
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> 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 <davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <austinzheng@gmail.com>
Cc: Leonardo Pessoa <me@lmpessoa.com>; swift-evolution
<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> 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 <me@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> 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> 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
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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.
This doesn't at all solve the parent issue of switching exhaustively, but switching over dynamicType distinguishes subclasses from base classes quite adequately:
class Foo { }
class Bar : Foo { }
class Baz : Foo { }
let b = Bar()
switch b.dynamicType {
case let t where t == Foo.self:
print("Foo!")
case let t where t == Bar.self:
print("Bar!")
case let t where t == Baz.self:
print("Baz!")
default:
print("Boo...")
}
It’s good to know about this capability, but it isn’t really a solution. It doesn’t give you a name bound to `b` with the appropriate type for the case. `t` is bound to `Bar.self`.
···
On May 25, 2016, at 1:09 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Wed, May 25, 2016 at 12:49 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>wrote:
On May 25, 2016, at 12:41 PM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:
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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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.
You use case instace.dynamicType == Dog.self. It's a bit more verbose, but makes sure that the exact type is matched.
I really wish, though for Xcode to feature something like the HHEnumeration plugin does - as soon as you type switch <variable>, it will create all the case statements. And with sealed classes, it could ensure the correct order so that some case isn't shadowed by the other. But that's kind of off topic.
···
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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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.
-Thorsten
···
Am 25.05.2016 um 19:49 schrieb Matthew Johnson via swift-evolution <swift-evolution@swift.org>:
Sent from my iPad
On May 25, 2016, at 12:41 PM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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.
You use case instace.dynamicType == Dog.self. It's a bit more verbose, but makes sure that the exact type is matched.
It could replace the `is` cast, but not the `as` cast where we get a name with the correct type bound to the value. If we’re going to introduce `asExactly` we might as well also introduce `isExactly` for consistency and convenience.
···
On May 25, 2016, at 1:09 PM, Charlie Monroe <charlie@charliemonroe.net> wrote:
I really wish, though for Xcode to feature something like the HHEnumeration plugin does - as soon as you type switch <variable>, it will create all the case statements. And with sealed classes, it could ensure the correct order so that some case isn't shadowed by the other. But that's kind of off topic.
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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
It could replace the `is` cast, but not the `as` cast where we get a name with the correct type bound to the value. If we’re going to introduce `asExactly` we might as well also introduce `isExactly` for consistency and convenience.
It can:
case let dog as Dog where dog.dynamicType == Dog.self:
Again, is a bit more verbose, but can be done.
···
I really wish, though for Xcode to feature something like the HHEnumeration plugin does - as soon as you type switch <variable>, it will create all the case statements. And with sealed classes, it could ensure the correct order so that some case isn't shadowed by the other. But that's kind of off topic.
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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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.
I don't think that solution is appropriate to Swift.
···
Sent from my iPad
On May 26, 2016, at 8:25 AM, Thorsten Seitz <tseitz42@icloud.com> wrote:
-Thorsten
Am 25.05.2016 um 19:49 schrieb Matthew Johnson via swift-evolution <swift-evolution@swift.org>:
Sent from my iPad
On May 25, 2016, at 12:41 PM, Charlie Monroe <charlie@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> 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
Sent: 24/05/2016 11:01 PM
To: Austin Zheng
Cc: Leonardo Pessoa; swift-evolution
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> 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 <me@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> 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> 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
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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.
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
···
Am 26.05.2016 um 15:40 schrieb Matthew Johnson <matthew@anandabits.com>:
On May 26, 2016, at 8:25 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
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 <charlie@charliemonroe.net <mailto:charlie@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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
It could replace the `is` cast, but not the `as` cast where we get a name with the correct type bound to the value. If we’re going to introduce `asExactly` we might as well also introduce `isExactly` for consistency and convenience.
It can:
case let dog as Dog where dog.dynamicType == Dog.self:
Again, is a bit more verbose, but can be done.
Sure, if you want to repeat the type twice. But good to keep in mind anyway. I think if we’re going to introduce exhaustive switch for classes and protocols we should make it roughly as convenient to use as for enums.
···
On May 25, 2016, at 3:02 PM, Charlie Monroe <charlie@charliemonroe.net> wrote:
I really wish, though for Xcode to feature something like the HHEnumeration plugin does - as soon as you type switch <variable>, it will create all the case statements. And with sealed classes, it could ensure the correct order so that some case isn't shadowed by the other. But that's kind of off topic.
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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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. 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).
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. 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.
If you want all non-leaf types to be abstract you should probably consider using protocols in Swift.
···
On May 26, 2016, at 9:52 AM, Thorsten Seitz <tseitz42@icloud.com> wrote:
Am 26.05.2016 um 15:40 schrieb Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>>:
On May 26, 2016, at 8:25 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
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 <charlie@charliemonroe.net <mailto:charlie@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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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
···
Am 26.05.2016 um 16:59 schrieb Matthew Johnson <matthew@anandabits.com>:
On May 26, 2016, at 9:52 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
Am 26.05.2016 um 15:40 schrieb Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>>:
On May 26, 2016, at 8:25 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
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 <charlie@charliemonroe.net <mailto:charlie@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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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.
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?
···
On May 26, 2016, at 10:13 AM, Thorsten Seitz <tseitz42@icloud.com> wrote:
Am 26.05.2016 um 16:59 schrieb Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>>:
On May 26, 2016, at 9:52 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
Am 26.05.2016 um 15:40 schrieb Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>>:
On May 26, 2016, at 8:25 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
-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 <charlie@charliemonroe.net <mailto:charlie@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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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
···
Am 26.05.2016 um 17:29 schrieb Matthew Johnson <matthew@anandabits.com>:
On May 26, 2016, at 10:13 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
Am 26.05.2016 um 16:59 schrieb Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>>:
On May 26, 2016, at 9:52 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
Am 26.05.2016 um 15:40 schrieb Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>>:
On May 26, 2016, at 8:25 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
-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 <charlie@charliemonroe.net <mailto:charlie@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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
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.
Yep
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.
Yeah, too cryptic. I’m continuing to try and think of something better. But Swift favors clarity over conciseness. I’m not sure we’ll find something more concise that is also at least as clear. Let me know if you think of anything else!
···
On May 26, 2016, at 10:45 AM, Thorsten Seitz <tseitz42@icloud.com> wrote:
Am 26.05.2016 um 17:29 schrieb Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>>:
On May 26, 2016, at 10:13 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
Am 26.05.2016 um 16:59 schrieb Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>>:
On May 26, 2016, at 9:52 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
Am 26.05.2016 um 15:40 schrieb Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>>:
On May 26, 2016, at 8:25 AM, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> wrote:
-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 <charlie@charliemonroe.net <mailto:charlie@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:davesweeris@mac.com>
Sent: 24/05/2016 11:01 PM
To: Austin Zheng <mailto:austinzheng@gmail.com>
Cc: Leonardo Pessoa <mailto:me@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 <me@lmpessoa.com <mailto:me@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
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
@anandabits Considering Swift classes are now sealed by default (requiring the open keyword to be extended outside their module) this sounds like a great proposal to reopen!