> On Feb 20, 2017, at 3:22 PM, Joe Groff <jgr...@apple.com> wrote:
> 
>> 
>> On Feb 20, 2017, at 1:04 PM, Matthew Johnson <matt...@anandabits.com 
>> <mailto:matt...@anandabits.com>> wrote:
>> 
>>> 
>>> On Feb 20, 2017, at 2:38 PM, Joe Groff <jgr...@apple.com 
>>> <mailto:jgr...@apple.com>> wrote:
>>> 
>>>> 
>>>> On Feb 20, 2017, at 7:32 AM, Matthew Johnson via swift-evolution 
>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>> 
>>>>> 
>>>>> On Feb 20, 2017, at 12:40 AM, Niels Andriesse via swift-evolution 
>>>>> <swift-evolution@swift.org <mailto:swift-evolution@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
>> 
>> Identical case types would definitely be a problem but I don’t think 
>> overlapping case types are always a problem.  I imagine this conversion 
>> working the same as any other ordinary overload resolution for ad-hoc 
>> overloads.
> 
> Conversions happen at runtime too. `0 as Any as? Either<Int, Int>` wouldn't 
> have any way to tell what `Either` to form if both arms of the Either were 
> subtype candidates. An Either<T, U> in <T, U> context can end up being bound 
> to Either<Int, Int> at runtime and interacting with runtime casts that way.

Hmm.  This is unfortunate.

In cases where T and U overlap and form a linear hierarchy but are not 
identical couldn’t the runtime determine the most direct path and choose that?

If the compiler prohibited cases with exactly the same types like `Either<Int, 
Int>` from being expressed statically how do these types end up getting formed 
dynamically?  Is there any way those operations could be failable?


> 
>> 
>>> —remember that enums are sums, not unions, and that's important for 
>>> composability and uniform behavior with generics. 
>> 
>> I’ve always thought of enums as nominal discriminated unions.  Maybe I’m 
>> using the wrong terminology.  Can you elaborate on the difference between 
>> sums and unions?  When you say union are you talking about the kind of thing 
>> some people have brought up in the past where any members in common are 
>> automatically made available on the union type?
> 
> Sums maintain structure whereas unions collapse it. As a sum, Optional<T> 
> maintains its shape even when T = Optional<U>. If it were a union, T u Nil u 
> Nil would collapse to T u Nil, losing the distinction between the inner and 
> outer nil and leading to problems in APIs that use the outer nil to 
> communicate meaning about some outer structure, such as asking for the 
> `first` element of a collection of Optionals.

Got it.  This is certainly a problem for `Optional`.

But sometimes this behavior of collapsing the syntactic specification to a 
canonical sum type would be very useful.  What is the reason we can’t have 
something syntactic type expressions like `Int | String`, `Int | String | 
String, `String | Int | String | Int`, etc all collapse to the same canonical 
structural sum type:

enum {
   sub case int(Int), string(String)
}

This is how I’ve been thinking about those syntactic types.  We already allow 
existential types to be formed using syntax that collapses to a canonical type:

typealias Existential1 = Protocol1 & Protocol2
typealias Existential2 = Protocol2 & Existential1 & Protocol 3 & Protocol1
typealias Existential3 = Existential1 & Protocol3

In this example Existential1 and Existential3 are different names for the same 
type.

Is there a reason we can’t have similar syntax that collapses to a similarly 
canonical sum type?  If we’re going to allow case subtypes this feels to me 
like a very natural and useful direction.  

If we don’t allow it there are two problems: people have to invent a largely 
meaningless name for the enum and it is incompatible with any other similarly 
structured enum.  Neither is a significant problem but they do add (seemingly) 
unnecessary friction to the language.

I wouldn’t expect these to be widely used - they would play a similar role as 
tuples - but they would be very appreciated where they are used.

> 
> -Joe

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to