[swift-evolution] Treating an Enum's Cases as Its Subtypes

Jonathan Hull jhull at gbis.com
Mon Feb 20 19:12:02 CST 2017


Just playing devil’s advocate for a second:
What if, instead of having an explicit annotation, we make a simple rule to break ambiguity.  That is, if the type is unambiguous, we use that, and if it is ambiguous, we use the first one (as defined by the creation/definition order). Then we have an explicit annotation to override that when it is wrong.

That said, I am on the fence about implicit conversion here.  I really like the ‘is .a’ notation, but automatically promoting from String/Int/Etc… might be unexpected in the wrong context.  I do want conversion in some cases, but as Joe says, there might be other cases where I would want to require explicit opt-in of some sort to enable implicit conversion.  I could see that opt-in potentially being on the whole enum though (like ‘indirect’).

I am on the fence though… I could be convinced.



> On Feb 20, 2017, at 12:38 PM, Joe Groff via swift-evolution <swift-evolution at swift.org> wrote:
> 
>> 
>> On Feb 20, 2017, at 7:32 AM, Matthew Johnson via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>>> 
>>> On Feb 20, 2017, at 12:40 AM, Niels Andriesse via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> I'd like to discuss the possibility of treating the cases of a given enum as if they are subtypes of that enum. This seems like a natural thing to do because enum cases (especially when they have associated values) effectively define a closed set of subtypes.
>>> 
>>> Doing so would allow for constructions such as the following:
>>> 
>>> enum Foo {
>>>   case a(name: String)
>>> }
>>> 
>>> func isA(foo: Foo) -> Bool {
>>>   // The old way:
>>>   if case .a = foo { return true }
>>>   return false
>>>   // The new way:
>>>   return foo is .a
>>> }
>>> 
>>> func printNameIfFooIsA(foo: Foo) -> Bool {
>>>   // The old way:
>>>   if case let .a(name) = foo {
>>>     print(name)
>>>   }
>>>   // The new way (1):
>>>   if let a = foo as? .a {
>>>     print(a.name <http://a.name/>)
>>>   }
>>>   // The new way (2):
>>>   if let name = (foo as? .a)?.name {
>>>     print(name)
>>>   }
>>> }
>>> 
>>> Treating an enum's cases as its subtypes would make enums easier to work with because handling them would be syntactically the same as handling other types.
>>> 
>>> The pattern matching capabilities of enums wouldn't be affected by this proposal.
>>> 
>>> Multiple other proposals have already attempted to simplify enum handling (they have particularly focused on getting rid of "if case" and adding the ability to treat enum case tests as expressions), but none of the solutions presented in those proposals have worked out so far.
>>> 
>>> I believe that this could be the right solution to multiple enum-related problems that have been brought up repeatedly.
>> 
>> I would like to see enum cases treated as subtypes of the enum type.  This is an interesting way to refer to the type of a case.  Unfortunately I don’t think it will work if we accept the proposal to give cases a compound name.  If we do that the name of this case becomes `a(name:)` which is not a valid type name.
> 
> I think there are definitely places where having cases be a subtype of an enum make sense, but I don't think it makes sense for *all* cases to be subtypes. For example, with "biased" containers like Optional and Result, it makes sense for the "right" side to be a subtype and the "wrong" side to be explicitly constructed, IMO. If the types of cases overlap, it would also be *ambiguous* which case ought to be constructed when the payload is converted to the enum type—remember that enums are sums, not unions, and that's important for composability and uniform behavior with generics. I would be fine allowing enum subtyping with some opt-in attribute, e.g.:
> 
> enum Optional<Wrapped> {
>   sub case some(wrapped)
>   case none
> }
> 
> enum Result<Wrapped> {
>   sub case ok(wrapped)
>   case error(Error) // not a subtype
> }
> 
> enum JSON {
>   // OK for these to all be sub-cases, since they don't overlap
>   sub case string(String), number(Double), array([JSON]), object([String: JSON]), null
> }
> 
> enum Either<T, U> {
>   // Error: sub cases potentially overlap
>   sub case left(T), right(U)
> }
> 
> -Joe
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170220/0fba6083/attachment.html>


More information about the swift-evolution mailing list