> 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