[swift-evolution] [Pitch] Enum with generic cases

Jaden Geller jaden.geller at gmail.com
Mon Apr 24 15:36:15 CDT 2017


> On Apr 24, 2017, at 1:15 PM, Kevin Nattinger via swift-evolution <swift-evolution at swift.org> wrote:
> 
> This makes it more convenient to create them, sure, but how would you pass them around or extract the value in a type-safe manner?

If the case introduces a generic type, than the switching over that case introduces a generic type variable in that scope.

> 
> e.g. now I can write:
> enum Thing<T, U> {
>     case thingOne(T)
>     case thingTwo(U)
> }
> 
> // How do I require thingOne<String> or thingTwo<Int>?
> func handle(thing: Thing<String, Int>) {
>     switch thing {
>     case .thingOne(let s): print("string \(s)")
>     case .thingTwo(let i): print("int \(i)")
>     }
> }
> 
> With your proposed syntax:
> 
> enum Thing {
>     case thingOne<T>(T)
>     case thingTwo<T>(T)
> }
> 
> func handle(thing: Thing) {
>     switch thing {
>     case thingOne(let s):
>         // What is the type of s?

It is some new generic variable `T`. It might be best to require that it is explicitly introduced:
```
    case thingOne<T>(let s):
```
You could use `s` like you could use `x` in `func <T>(x: T) { … }`. That’s to say that you couldn’t do much with it. You could store it in an `Any`. You could dynamically check the type. You could ignore it. You could print it.

It becomes a lot more useful when there are constraints on `T` in the original case definition of the type. For example, if `case thingOne<T: FloatingPoint>(T)` were written in the original enum definition, you could do anything with `s` that you could do with a `FloatingPoint`.

>     case thingTwo<Int>(let i):
>         // is it even possible to write an exhaustive switch?

Sure, but this switch isn’t yet exhaustive. You’d also have to add another case:
```
    case thingTwo<T>(let i):
```
When we do come back to this proposal, it might be reasonable to leave these sorts of specializations (e.g. `case thingTwo<Int>`) out of the initial design since they might significantly complicate the model and they are entirely additive. I’m not sure though, I’m not familiar with the implementation.

>     }
> }
> 

I’d really love to see GADTs in Swift, even if in a more limited form! Another GADT feature that would be nice to add (though probably would deserve a separate, later proposal) would be specialized generic return types for each case:

```
enum Expr<T> {
    case value(T) -> Expr<T>
    case plus(Expr<Int>, Expr<Int>) -> Expr<Int>
    case and(Expr<Bool>, Expr<Bool>) -> Expr<Bool>
    case equals(Expr<T>, Expr<T>) -> Expr<T> where T: Equatable
}
```

But ya, I realize this is a very major complication of the model… Would be so cool though!

This is all definitely out of scope for Swift 4, and likely out of scope for Swift 5 even… I guess we’ll find out once we get there. Still fun to think about!

Cheers,
Jaden Geller

> 
> 
>> On Apr 24, 2017, at 6:57 AM, Joshua Alvarado via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> Here is my pitch on adding generics to enum cases and not to the enum type itself. Let me know if you have an improvements or modifications lets open it to discussion thank you swiftys! :)
>> 
>> Enum with generic cases
>> 
>> Proposal: SE-NNNN <https://github.com/lostatseajoshua/swift-evolution/blob/master/NNNN-enum-generic-cases.md>
>> Authors: Joshua Alvarado <https://github.com/alvaradojoshua0>
>> Review Manager: TBD
>> Status: PITCH
>> During the review process, add the following fields as needed:
>> 
>> Decision Notes: Rationale <https://lists.swift.org/pipermail/swift-evolution/>, Additional Commentary <https://lists.swift.org/pipermail/swift-evolution/>
>> Bugs: SR-NNNN <https://bugs.swift.org/browse/SR-NNNN>, SR-MMMM <https://bugs.swift.org/browse/SR-MMMM>
>> Previous Revision: 1 <https://github.com/apple/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md>
>> Previous Proposal: SE-XXXX <https://github.com/lostatseajoshua/swift-evolution/blob/master/XXXX-filename.md>
>>  <https://github.com/lostatseajoshua/swift-evolution/tree/master#introduction>Introduction
>> 
>> This proposal adds a change to the enumeration type that allows an enum case to cast a generic on its associated value.
>> 
>> Swift-evolution thread: Discussion thread topic for that proposal <https://lists.swift.org/pipermail/swift-evolution/>
>>  <https://github.com/lostatseajoshua/swift-evolution/tree/master#motivation>Motivation
>> 
>> Enums currently support generics, but they are added onto the type itself. This can cause adverse syntax when implementing generics for associated values to be stored along each case. The enum case holds the associated value (not the enum type itself) so should create its own value constraints.
>> 
>>  <https://github.com/lostatseajoshua/swift-evolution/tree/master#proposed-solution>Proposed solution
>> 
>> The generic is to be casted on the case of the enum and not on the enum itself.
>> 
>>  <https://github.com/lostatseajoshua/swift-evolution/tree/master#detailed-design>Detailed design
>> 
>> Current implementation:
>> 
>> // enum with two generic types
>> enum Foo<T: Hashable, U: Collection> {
>>     case bar(obj: T)
>>     case baz(obj: U)
>> }
>> 
>> // U is to be casted but it is not even used
>> let foo: Foo<String, [String]> = .bar(obj: "hash")
>> 
>> // Creating an optional enum, the generics have to be casted without a value set
>> // The casting is really not needed as the values should be casted not the enum
>> var foo1: Foo<String, [String]>?
>> 
>> // Collections don’t look great either
>> var foos = [Foo<String, [String]>]()
>> foos.append(.bar(obj:"hash"))
>> Proposed solution
>> 
>> enum Foo {
>>     case bar<T: Hashable>(obj: T)
>>     case baz<U: Collection>(obj: U)
>> }
>> 
>> // generic type inferred on T
>> var foo: Foo = .bar(obj: "hash") 
>> 
>> // doesn’t need to cast the generic on the optional enum
>> // the associated value will hold the cast
>> var foo1: Foo? 
>> 
>> // This also gives better syntax with collections of enums with associated types
>> var foos = [Foo]()
>> foos.append(.bar(obj: "hey")
>>  <https://github.com/lostatseajoshua/swift-evolution/tree/master#source-compatibility>Source compatibility
>> 
>> This may cause subtle breaking changes for areas in code with generic enum cases. The compiler could help with the change by finding the associated generic and updating the case with the new syntax.
>> 
>>  <https://github.com/lostatseajoshua/swift-evolution/tree/master#alternatives-considered>Alternatives considered
>> 
>> An alternative would be to extend the associatedtype keyword to the enum type.
>> 
>> enum Foo {
>>     associatedtype T = Hashable
>>     case bar(obj: T)
>> }
>> 
>> Copy of proposal can be found here Swift proposal on github <https://github.com/lostatseajoshua/swift-evolution/blob/master/NNNN-enum-generic-cases.md>
>> 
>> -- 
>> Joshua Alvarado
>> alvaradojoshua0 at gmail.com <mailto:alvaradojoshua0 at gmail.com>_______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170424/8817a138/attachment.html>


More information about the swift-evolution mailing list