Enum with generic cases

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 typesenum Foo<T: Hashable, U: Collection> {
    case bar(obj: T)
    case baz(obj: U)
}
// U is to be casted but it is not even usedlet 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 enumvar foo1: Foo<String, [String]>?
// Collections don’t look great eithervar 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 Tvar foo: Foo = .bar(obj: "hash")
// doesn’t need to cast the generic on the optional enum// the
associated value will hold the castvar foo1: Foo?
// This also gives better syntax with collections of enums with
associated typesvar 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@gmail.com

2 Likes

/me Pushes implementation detail related concerns out of head

+1

I want this feature but I seriously doubt that it is feasible at the
moment. There are so many 'more pressing' features that, even if this were
accepted now, it wouldn't be implemented it in time for Swift 4.

That said, I would love to be incorrect.

···

On Mon, Apr 24, 2017 at 9:57 AM, Joshua Alvarado via swift-evolution < swift-evolution@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 typesenum Foo<T: Hashable, U: Collection> {
    case bar(obj: T)
    case baz(obj: U)
}
// U is to be casted but it is not even usedlet 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 enumvar foo1: Foo<String, [String]>?
// Collections don’t look great eithervar 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 Tvar foo: Foo = .bar(obj: "hash")
// doesn’t need to cast the generic on the optional enum// the associated value will hold the castvar foo1: Foo?
// This also gives better syntax with collections of enums with associated typesvar 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@gmail.com

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

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?

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?
    case thingTwo<Int>(let i):
        // is it even possible to write an exhaustive switch?
    }
}

···

On Apr 24, 2017, at 6:57 AM, Joshua Alvarado via swift-evolution <swift-evolution@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! :slight_smile:

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@gmail.com <mailto:alvaradojoshua0@gmail.com>_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

1 Like

This is somehow similar to https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-constants.

Question, will pattern matching still work fine with that? I kinda fear that this won’t work in a different scopes, but feel free to prove me being wrong.

enum Foo {
    case bar<T: Hashable>(obj: T)
    case baz<U: Collection>(obj: U)
}

struct Test {
     
    var foo: Foo = .bar(obj: "swift")
     
    func test() {
         
        switch self.foo {
        case /* Check for `bar` and String */: …
        case /* How to pattern match against `baz`? What is `U`? */: …
         
        // Do we need `default` everytime?
        }
    }
}

···

--
Adrian Zubarev
Sent with Airmail

Am 24. April 2017 um 15:57:33, Joshua Alvarado via swift-evolution (swift-evolution@swift.org) schrieb:

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! :slight_smile:
Enum with generic cases
Proposal: SE-NNNN
Authors: Joshua Alvarado
Review Manager: TBD
Status: PITCH
During the review process, add the following fields as needed:

Decision Notes: Rationale, Additional Commentary
Bugs: SR-NNNN, SR-MMMM
Previous Revision: 1
Previous Proposal: SE-XXXX
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

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.

Proposed solution

The generic is to be casted on the case of the enum and not on the enum itself.

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")
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.

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

--
Joshua Alvarado
alvaradojoshua0@gmail.com
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

/me Pushes implementation detail related concerns out of head

+1

I want this feature but I seriously doubt that it is feasible at the moment. There are so many 'more pressing' features that, even if this were accepted now, it wouldn't be implemented it in time for Swift 4.

Out of scope for Swift 4, certainly. It may not look like it, but this is a fairly large feature. I suggest reading up on generalized algebraic data types <https://en.wikipedia.org/wiki/Generalized_algebraic_data_type> (GADTs), which is the more programming-language-theoretical name for what you’re describing here.

  - Doug

···

On Apr 24, 2017, at 7:23 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

That said, I would love to be incorrect.

On Mon, Apr 24, 2017 at 9:57 AM, Joshua Alvarado via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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! :slight_smile:

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@gmail.com <mailto:alvaradojoshua0@gmail.com>
_______________________________________________
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
https://lists.swift.org/mailman/listinfo/swift-evolution

2 Likes

I think the pattern match would have to introduce a new generic type variable within the match scope. Definitely not a simple feature, but GADTs would be so cool!

···

On Apr 24, 2017, at 7:57 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

This is somehow similar to https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-constants.

Question, will pattern matching still work fine with that? I kinda fear that this won’t work in a different scopes, but feel free to prove me being wrong.

enum Foo {
    case bar<T: Hashable>(obj: T)
    case baz<U: Collection>(obj: U)
}

struct Test {
     
    var foo: Foo = .bar(obj: "swift")
     
    func test() {
         
        switch self.foo {
        case /* Check for `bar` and String */: …
        case /* How to pattern match against `baz`? What is `U`? */: …
         
        // Do we need `default` everytime?
        }
    }
}

--
Adrian Zubarev
Sent with Airmail

Am 24. April 2017 um 15:57:33, Joshua Alvarado via swift-evolution (swift-evolution@swift.org) schrieb:

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! :slight_smile:
Enum with generic cases
Proposal: SE-NNNN
Authors: Joshua Alvarado
Review Manager: TBD
Status: PITCH
During the review process, add the following fields as needed:

Decision Notes: Rationale, Additional Commentary
Bugs: SR-NNNN, SR-MMMM
Previous Revision: 1
Previous Proposal: SE-XXXX
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

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.

Proposed solution

The generic is to be casted on the case of the enum and not on the enum itself.

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")
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.

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

--
Joshua Alvarado
alvaradojoshua0@gmail.com
_______________________________________________
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

1 Like

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 1:15 PM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 24, 2017, at 6:57 AM, Joshua Alvarado via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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! :slight_smile:

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@gmail.com <mailto:alvaradojoshua0@gmail.com>_______________________________________________
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
https://lists.swift.org/mailman/listinfo/swift-evolution

1 Like

When would be a good time to resubmit this proposal for discussion? Or can
I still proceed with the review and it possibly gets deferred but at least
be put in the pipeline (if deferred)?

···

On Mon, Apr 24, 2017 at 9:07 AM, Douglas Gregor <dgregor@apple.com> wrote:

On Apr 24, 2017, at 7:23 AM, T.J. Usiyan via swift-evolution < > swift-evolution@swift.org> wrote:

/me Pushes implementation detail related concerns out of head

+1

I want this feature but I seriously doubt that it is feasible at the
moment. There are so many 'more pressing' features that, even if this were
accepted now, it wouldn't be implemented it in time for Swift 4.

Out of scope for Swift 4, certainly. It may not look like it, but this is
a fairly large feature. I suggest reading up on generalized algebraic
data types <https://en.wikipedia.org/wiki/Generalized_algebraic_data_type> (GADTs),
which is the more programming-language-theoretical name for what you’re
describing here.

- Doug

That said, I would love to be incorrect.

On Mon, Apr 24, 2017 at 9:57 AM, Joshua Alvarado via swift-evolution < > swift-evolution@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 typesenum Foo<T: Hashable, U: Collection> {
    case bar(obj: T)
    case baz(obj: U)
}
// U is to be casted but it is not even usedlet 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 enumvar foo1: Foo<String, [String]>?
// Collections don’t look great eithervar 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 Tvar foo: Foo = .bar(obj: "hash")
// doesn’t need to cast the generic on the optional enum// the associated value will hold the castvar foo1: Foo?
// This also gives better syntax with collections of enums with associated typesvar 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@gmail.com

_______________________________________________
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

--
Joshua Alvarado
alvaradojoshua0@gmail.com

Well in your case thing one and thing two will be the same type as you are
using the same T generic type on both.

To achieve your case you can do an extension on the enum and use two
different generics:

enum Thing {
    case thingOne<T>(T)
    case thingTwo<U>(U)
}

extension Thing where T == String, U == Int {
   func handle(thing: Thing) {
    switch thing {
    case thingOne(let s):
        // s is type String

    case thingTwo(let i):
        // i is an Int
    }
   }
}

This can actually be achieved in Swift 3.1, you can run this in a
playground.

enum Foo<T, U> {

    case bar(obj: T)

    case baz(obj: U)

    func handle() {

        switch self {

        case .bar(obj: let x):

            break

        case .baz(obj: let y):

            break

        }

    }

}

extension Foo where T == String, U == Int {

    func handle() {

        switch self {

        case .bar(obj: let str):

            print(str)

        case .baz(obj: let aNum):

            print(aNum)

        }

    }

}

let foo = Foo<String, Int>.baz(obj: 1)

foo.handle() // prints 1

···

On Mon, Apr 24, 2017 at 2:15 PM, Kevin Nattinger <swift@nattinger.net> 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?

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?
    case thingTwo<Int>(let i):
        // is it even possible to write an exhaustive switch?
    }
}

On Apr 24, 2017, at 6:57 AM, Joshua Alvarado via swift-evolution < > swift-evolution@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 typesenum Foo<T: Hashable, U: Collection> {
    case bar(obj: T)
    case baz(obj: U)
}
// U is to be casted but it is not even usedlet 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 enumvar foo1: Foo<String, [String]>?
// Collections don’t look great eithervar 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 Tvar foo: Foo = .bar(obj: "hash")
// doesn’t need to cast the generic on the optional enum// the associated value will hold the castvar foo1: Foo?
// This also gives better syntax with collections of enums with associated typesvar 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@gmail.com
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Joshua Alvarado
alvaradojoshua0@gmail.com

Well in your case thing one and thing two will be the same type as you are using the same T generic type on both.

To achieve your case you can do an extension on the enum and use two different generics:

enum Thing {
    case thingOne<T>(T)
    case thingTwo<U>(U)
}

extension Thing where T == String, U == Int {
   func handle(thing: Thing) {
    switch thing {
    case thingOne(let s):
        // s is type String
        
    case thingTwo(let i):
        // i is an Int
    }
   }
}

This can actually be achieved in Swift 3.1, you can run this in a playground.

This is not quite the same. In the original example, the type information was thrown away after an instance of type `Foo` was constructed.

Here’s an example that wouldn’t work in your model:

enum Whatever {
    case some<T>(T)
}

var x: Whatever = .some(3)
x = .some([1, 2, 3])

If `Whatever` was generic in `T`, then a variable can only store case payloads with that specific type. I guess this could be worked around by introducing a `AnyWhatever` protocol, but still, it’s not an identical feature.

That said, it might be true that this isn’t actually a desirable feature. We ought to definitely consider the implications of it and any alternatives. Thanks for mentioning this, it’s a decent workaround dependent on what you’d like to accomplish!

Cheers,
Jaden Geller

···

On Apr 24, 2017, at 1:34 PM, Joshua Alvarado via swift-evolution <swift-evolution@swift.org> wrote:

enum Foo<T, U> {
    case bar(obj: T)
    case baz(obj: U)

    func handle() {
        switch self {
        case .bar(obj: let x):
            break
        case .baz(obj: let y):
            break
        }
    }
}

extension Foo where T == String, U == Int {
    func handle() {
        switch self {
        case .bar(obj: let str):
            print(str)
        case .baz(obj: let aNum):
            print(aNum)
        }
    }
}

let foo = Foo<String, Int>.baz(obj: 1)
foo.handle() // prints 1

On Mon, Apr 24, 2017 at 2:15 PM, Kevin Nattinger <swift@nattinger.net <mailto:swift@nattinger.net>> 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?

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?
    case thingTwo<Int>(let i):
        // is it even possible to write an exhaustive switch?
    }
}

On Apr 24, 2017, at 6:57 AM, Joshua Alvarado via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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! :slight_smile:

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@gmail.com <mailto:alvaradojoshua0@gmail.com>_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Joshua Alvarado
alvaradojoshua0@gmail.com <mailto:alvaradojoshua0@gmail.com>_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Well in your case thing one and thing two will be the same type as you are using the same T generic type on both.

It’s two different and unrelated generics unless you’re intending to change the way generics work entirely.

func foo<T>(x: T)
func bar<T>(x: T)
They both use “T” as the generic, but the T’s are unrelated.

You can even have a generic function type shadow the containing class’s generic type:

enum Thing<T, U> {
    case thingOne(T)
    case thingTwo(U)

    func foo<T>(x: T) -> T { return x }
}

let x = Thing<String, Int>.thingOne("").foo(x: [5.0])
type(of: x) // Array<Double>.Type

(Not recommended, obviously, just demonstrating that generic types are independent regardless of the name)

To achieve your case you can do an extension on the enum and use two different generics:

enum Thing {
    case thingOne<T>(T)
    case thingTwo<U>(U)
}

extension Thing where T == String, U == Int {

Ah, but there is no T or U on the enum, just on the cases. And what if the function needs to be in another class for encapsulation reasons?
Furthermore, how would you even call this function? How do you create a variable that can hold either a thingOne with a string or a thingTwo with an Int, and nothing else?

···

On Apr 24, 2017, at 1:34 PM, Joshua Alvarado <alvaradojoshua0@gmail.com> wrote:

   func handle(thing: Thing) {
    switch thing {
    case thingOne(let s):
        // s is type String
        
    case thingTwo(let i):
        // i is an Int
    }
   }
}

This can actually be achieved in Swift 3.1, you can run this in a playground.

enum Foo<T, U> {
    case bar(obj: T)
    case baz(obj: U)

    func handle() {
        switch self {
        case .bar(obj: let x):
            break
        case .baz(obj: let y):
            break
        }
    }
}

extension Foo where T == String, U == Int {
    func handle() {
        switch self {
        case .bar(obj: let str):
            print(str)
        case .baz(obj: let aNum):
            print(aNum)
        }
    }
}

let foo = Foo<String, Int>.baz(obj: 1)
foo.handle() // prints 1

On Mon, Apr 24, 2017 at 2:15 PM, Kevin Nattinger <swift@nattinger.net <mailto:swift@nattinger.net>> 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?

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?
    case thingTwo<Int>(let i):
        // is it even possible to write an exhaustive switch?
    }
}

On Apr 24, 2017, at 6:57 AM, Joshua Alvarado via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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! :slight_smile:

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@gmail.com <mailto:alvaradojoshua0@gmail.com>_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Joshua Alvarado
alvaradojoshua0@gmail.com <mailto:alvaradojoshua0@gmail.com>

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.

I don’t want a generic type, I want to be able to specify exactly what types the function accepts.

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.

So what good is a value I can’t do anything with?

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):

In my original code, the function specifies exactly what it can handle, and that restriction is enforced by the compiler. Do you really think it would be an improvement to remove compile-time type restrictions? We may as well get rid of the type system and duck-type like python. At least that would speed up type-inference bottlenecks.

···

On Apr 24, 2017, at 1:36 PM, Jaden Geller <jaden.geller@gmail.com> wrote:

On Apr 24, 2017, at 1:15 PM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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@swift.org <mailto:swift-evolution@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! :slight_smile:

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@gmail.com <mailto:alvaradojoshua0@gmail.com>_______________________________________________
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

Hmmm interesting point it would be good to consider this and get to an implementation that helps cover both or just implement the desired feature and in the future add more resiliency.

Alvarado, Joshua

···

On Apr 24, 2017, at 2:41 PM, Jaden Geller <jaden.geller@gmail.com> wrote:

On Apr 24, 2017, at 1:34 PM, Joshua Alvarado via swift-evolution <swift-evolution@swift.org> wrote:

Well in your case thing one and thing two will be the same type as you are using the same T generic type on both.

To achieve your case you can do an extension on the enum and use two different generics:

enum Thing {
    case thingOne<T>(T)
    case thingTwo<U>(U)
}

extension Thing where T == String, U == Int {
   func handle(thing: Thing) {
    switch thing {
    case thingOne(let s):
        // s is type String
        
    case thingTwo(let i):
        // i is an Int
    }
   }
}

This can actually be achieved in Swift 3.1, you can run this in a playground.

This is not quite the same. In the original example, the type information was thrown away after an instance of type `Foo` was constructed.

Here’s an example that wouldn’t work in your model:

enum Whatever {
    case some<T>(T)
}

var x: Whatever = .some(3)
x = .some([1, 2, 3])

If `Whatever` was generic in `T`, then a variable can only store case payloads with that specific type. I guess this could be worked around by introducing a `AnyWhatever` protocol, but still, it’s not an identical feature.

That said, it might be true that this isn’t actually a desirable feature. We ought to definitely consider the implications of it and any alternatives. Thanks for mentioning this, it’s a decent workaround dependent on what you’d like to accomplish!

Cheers,
Jaden Geller

enum Foo<T, U> {
    case bar(obj: T)
    case baz(obj: U)

    func handle() {
        switch self {
        case .bar(obj: let x):
            break
        case .baz(obj: let y):
            break
        }
    }
}

extension Foo where T == String, U == Int {
    func handle() {
        switch self {
        case .bar(obj: let str):
            print(str)
        case .baz(obj: let aNum):
            print(aNum)
        }
    }
}

let foo = Foo<String, Int>.baz(obj: 1)
foo.handle() // prints 1

On Mon, Apr 24, 2017 at 2:15 PM, Kevin Nattinger <swift@nattinger.net> 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?

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?
    case thingTwo<Int>(let i):
        // is it even possible to write an exhaustive switch?
    }
}

On Apr 24, 2017, at 6:57 AM, Joshua Alvarado via swift-evolution <swift-evolution@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! :slight_smile:
Enum with generic cases
Proposal: SE-NNNN
Authors: Joshua Alvarado
Review Manager: TBD
Status: PITCH
During the review process, add the following fields as needed:

Decision Notes: Rationale, Additional Commentary
Bugs: SR-NNNN, SR-MMMM
Previous Revision: 1
Previous Proposal: SE-XXXX
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

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.

Proposed solution

The generic is to be casted on the case of the enum and not on the enum itself.

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")
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.

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

--
Joshua Alvarado
alvaradojoshua0@gmail.com
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Joshua Alvarado
alvaradojoshua0@gmail.com
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Yeah I was implying that T would be the same generic for both so that is a good point for my proposal they would have to be disconnected or maybe he complier can infer that the two generics are the same within the same enum scope.

A way to create a variable of thing one with an aunt or string only would be to use a case let but it would have to be two spectate cases and not just one variable.

If case let .thingOne(str as String) {

}

If case let .thingOne(aNum as Int) {

}

Alvarado, Joshua

···

On Apr 24, 2017, at 2:56 PM, Kevin Nattinger <swift@nattinger.net> wrote:

On Apr 24, 2017, at 1:34 PM, Joshua Alvarado <alvaradojoshua0@gmail.com> wrote:

Well in your case thing one and thing two will be the same type as you are using the same T generic type on both.

It’s two different and unrelated generics unless you’re intending to change the way generics work entirely.

func foo<T>(x: T)
func bar<T>(x: T)
They both use “T” as the generic, but the T’s are unrelated.

You can even have a generic function type shadow the containing class’s generic type:

enum Thing<T, U> {
    case thingOne(T)
    case thingTwo(U)

    func foo<T>(x: T) -> T { return x }
}

let x = Thing<String, Int>.thingOne("").foo(x: [5.0])
type(of: x) // Array<Double>.Type

(Not recommended, obviously, just demonstrating that generic types are independent regardless of the name)

To achieve your case you can do an extension on the enum and use two different generics:

enum Thing {
    case thingOne<T>(T)
    case thingTwo<U>(U)
}

extension Thing where T == String, U == Int {

Ah, but there is no T or U on the enum, just on the cases. And what if the function needs to be in another class for encapsulation reasons?
Furthermore, how would you even call this function? How do you create a variable that can hold either a thingOne with a string or a thingTwo with an Int, and nothing else?

   func handle(thing: Thing) {
    switch thing {
    case thingOne(let s):
        // s is type String
        
    case thingTwo(let i):
        // i is an Int
    }
   }
}

This can actually be achieved in Swift 3.1, you can run this in a playground.

enum Foo<T, U> {
    case bar(obj: T)
    case baz(obj: U)

    func handle() {
        switch self {
        case .bar(obj: let x):
            break
        case .baz(obj: let y):
            break
        }
    }
}

extension Foo where T == String, U == Int {
    func handle() {
        switch self {
        case .bar(obj: let str):
            print(str)
        case .baz(obj: let aNum):
            print(aNum)
        }
    }
}

let foo = Foo<String, Int>.baz(obj: 1)
foo.handle() // prints 1

On Mon, Apr 24, 2017 at 2:15 PM, Kevin Nattinger <swift@nattinger.net> 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?

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?
    case thingTwo<Int>(let i):
        // is it even possible to write an exhaustive switch?
    }
}

On Apr 24, 2017, at 6:57 AM, Joshua Alvarado via swift-evolution <swift-evolution@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! :slight_smile:
Enum with generic cases
Proposal: SE-NNNN
Authors: Joshua Alvarado
Review Manager: TBD
Status: PITCH
During the review process, add the following fields as needed:

Decision Notes: Rationale, Additional Commentary
Bugs: SR-NNNN, SR-MMMM
Previous Revision: 1
Previous Proposal: SE-XXXX
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

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.

Proposed solution

The generic is to be casted on the case of the enum and not on the enum itself.

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")
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.

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

--
Joshua Alvarado
alvaradojoshua0@gmail.com
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Joshua Alvarado
alvaradojoshua0@gmail.com

“Out of scope for Swift 4” means that there is no time for it now and the community should focus on features and issues yet to be solved in Swift 4. In other words it means that you should defer the proposal at least until Swift 4 is released.

···

--
Adrian Zubarev
Sent with Airmail

Am 24. April 2017 um 21:57:13, Joshua Alvarado via swift-evolution (swift-evolution@swift.org) schrieb:

When would be a good time to resubmit this proposal for discussion? Or can I still proceed with the review and it possibly gets deferred but at least be put in the pipeline (if deferred)?

On Mon, Apr 24, 2017 at 9:07 AM, Douglas Gregor <dgregor@apple.com> wrote:

On Apr 24, 2017, at 7:23 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

/me Pushes implementation detail related concerns out of head

+1

I want this feature but I seriously doubt that it is feasible at the moment. There are so many 'more pressing' features that, even if this were accepted now, it wouldn't be implemented it in time for Swift 4.

Out of scope for Swift 4, certainly. It may not look like it, but this is a fairly large feature. I suggest reading up on generalized algebraic data types (GADTs), which is the more programming-language-theoretical name for what you’re describing here.

- Doug

That said, I would love to be incorrect.

On Mon, Apr 24, 2017 at 9:57 AM, Joshua Alvarado via swift-evolution <swift-evolution@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! :slight_smile:
Enum with generic cases
Proposal: SE-NNNN
Authors: Joshua Alvarado
Review Manager: TBD
Status: PITCH
During the review process, add the following fields as needed:

Decision Notes: Rationale, Additional Commentary
Bugs: SR-NNNN, SR-MMMM
Previous Revision: 1
Previous Proposal: SE-XXXX
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

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.

Proposed solution

The generic is to be casted on the case of the enum and not on the enum itself.

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")
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.

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

--
Joshua Alvarado
alvaradojoshua0@gmail.com

_______________________________________________
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

--
Joshua Alvarado
alvaradojoshua0@gmail.com
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

When would be a good time to resubmit this proposal for discussion?

Sometime in the summer we’re going to start talking about the scope of Swift post-4.0.

Or can I still proceed with the review and it possibly gets deferred but at least be put in the pipeline (if deferred)?

We try not to let the review process get *too* far ahead of the actual implementation, because detailed design review for acceptance is itself a time-consuming process.

  - Doug

···

On Apr 24, 2017, at 12:57 PM, Joshua Alvarado <alvaradojoshua0@gmail.com> wrote:

On Mon, Apr 24, 2017 at 9:07 AM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Apr 24, 2017, at 7:23 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

/me Pushes implementation detail related concerns out of head

+1

I want this feature but I seriously doubt that it is feasible at the moment. There are so many 'more pressing' features that, even if this were accepted now, it wouldn't be implemented it in time for Swift 4.

Out of scope for Swift 4, certainly. It may not look like it, but this is a fairly large feature. I suggest reading up on generalized algebraic data types <https://en.wikipedia.org/wiki/Generalized_algebraic_data_type> (GADTs), which is the more programming-language-theoretical name for what you’re describing here.

  - Doug

That said, I would love to be incorrect.

On Mon, Apr 24, 2017 at 9:57 AM, Joshua Alvarado via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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! :slight_smile:

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@gmail.com <mailto:alvaradojoshua0@gmail.com>
_______________________________________________
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

--
Joshua Alvarado
alvaradojoshua0@gmail.com <mailto:alvaradojoshua0@gmail.com>

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.

I don’t want a generic type, I want to be able to specify exactly what types the function accepts.

Generic cases are exactly what I understand to be proposed. Can you explain how what you're looking for differs?

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.

So what good is a value I can’t do anything with?

It's not particularly useful BECAUSE you didn't specify any generic constraints. Just like a generic function, you can only do with a value what it's constraints allow.

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):

In my original code, the function specifies exactly what it can handle, and that restriction is enforced by the compiler. Do

Your switch statement wasn't exhaustive. You need to add an additional case that handles a T that isn't an Int (because it might not be one)! Swift cannot statically know the payload since it isn't part of the type. The case generics are erased, and you need to use case matching to bring them back.

you really think it would be an improvement to remove compile-time type restrictions? We may as well get rid of the type system and duck-type like python. At least that would speed up type-inference bottlenecks.

That's entirely not what I advocated. I don't appreciate you strawmanning my explanation.

The model I explained is consistent with Swift's current type checking. It certainly does not remove any restrictions or add duck typing.

How can I improve your understanding?

···

On Apr 24, 2017, at 2:09 PM, Kevin Nattinger <swift@nattinger.net> wrote:

On Apr 24, 2017, at 1:36 PM, Jaden Geller <jaden.geller@gmail.com> wrote:
On Apr 24, 2017, at 1:15 PM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org> wrote:

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@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! :slight_smile:

Enum with generic cases

Proposal: SE-NNNN
Authors: Joshua Alvarado
Review Manager: TBD
Status: PITCH
During the review process, add the following fields as needed:

Decision Notes: Rationale, Additional Commentary
Bugs: SR-NNNN, SR-MMMM
Previous Revision: 1
Previous Proposal: SE-XXXX
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

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.

Proposed solution

The generic is to be casted on the case of the enum and not on the enum itself.

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")
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.

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

--
Joshua Alvarado
alvaradojoshua0@gmail.com
_______________________________________________
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

Hmmm interesting point it would be good to consider this and get to an implementation that helps cover both or just implement the desired feature and in the future add more resiliency.

Alvarado, Joshua

Well in your case thing one and thing two will be the same type as you are using the same T generic type on both.

To achieve your case you can do an extension on the enum and use two different generics:

enum Thing {
    case thingOne<T>(T)
    case thingTwo<U>(U)
}

extension Thing where T == String, U == Int {
   func handle(thing: Thing) {
    switch thing {
    case thingOne(let s):
        // s is type String
        
    case thingTwo(let i):
        // i is an Int
    }
   }
}

I think it’s important to not that the above example (without generic information carried with the type) should not be able to provide an extension that constrains the type. The problem is that, since we don’t know what `T` and `U` actually are for an arbitrary instance at compile-time, we cannot ensure that `handle` is not called when `T` and `U` don’t match the extension. We’d have to require an implementation of `handle` for every possible type. I guess it _could_ be reasonable to allow such a constrained extension if we already defined `handle` for any `T` and `U`, but this implementation would probably be simple (and equivalent after optimization):

enum Thing {
    case thingOne<T>(T)
    case thingTwo<U>(U)
    
    func handle() {
        switch self {
        case .thingOne<T>(let s):
            if T is String {
                // s is of type String
            } else {
                // default case!
            }
        case .thingTwo<U>(let i):
            if U is Int {
                // i is an Int
            } else {
                // default case!
            }
        }
    }
}

Cheers,
Jaden Geller

···

On Apr 24, 2017, at 2:33 PM, Joshua Alvarado <alvaradojoshua0@gmail.com> wrote:
On Apr 24, 2017, at 2:41 PM, Jaden Geller <jaden.geller@gmail.com <mailto:jaden.geller@gmail.com>> wrote:

On Apr 24, 2017, at 1:34 PM, Joshua Alvarado via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This can actually be achieved in Swift 3.1, you can run this in a playground.

This is not quite the same. In the original example, the type information was thrown away after an instance of type `Foo` was constructed.

Here’s an example that wouldn’t work in your model:

enum Whatever {
    case some<T>(T)
}

var x: Whatever = .some(3)
x = .some([1, 2, 3])

If `Whatever` was generic in `T`, then a variable can only store case payloads with that specific type. I guess this could be worked around by introducing a `AnyWhatever` protocol, but still, it’s not an identical feature.

That said, it might be true that this isn’t actually a desirable feature. We ought to definitely consider the implications of it and any alternatives. Thanks for mentioning this, it’s a decent workaround dependent on what you’d like to accomplish!

Cheers,
Jaden Geller

enum Foo<T, U> {
    case bar(obj: T)
    case baz(obj: U)

    func handle() {
        switch self {
        case .bar(obj: let x):
            break
        case .baz(obj: let y):
            break
        }
    }
}

extension Foo where T == String, U == Int {
    func handle() {
        switch self {
        case .bar(obj: let str):
            print(str)
        case .baz(obj: let aNum):
            print(aNum)
        }
    }
}

let foo = Foo<String, Int>.baz(obj: 1)
foo.handle() // prints 1

On Mon, Apr 24, 2017 at 2:15 PM, Kevin Nattinger <swift@nattinger.net <mailto:swift@nattinger.net>> 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?

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?
    case thingTwo<Int>(let i):
        // is it even possible to write an exhaustive switch?
    }
}

On Apr 24, 2017, at 6:57 AM, Joshua Alvarado via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@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! :slight_smile:

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@gmail.com <mailto:alvaradojoshua0@gmail.com>_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Joshua Alvarado
alvaradojoshua0@gmail.com <mailto:alvaradojoshua0@gmail.com>_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

   func handle() {
        switch self {
        case .thingOne<T>(let s):
            if T is String {
                // s is of type String
            } else {
                // default case!
            }
        case .thingTwo<U>(let i):
            if U is Int {
                // i is an Int
            } else {
                // default case!
            }
        }
    }

This would definitely be the default case if the generics don't have any constraints on them but the proposal is to make generics in enumerated better so that the above won't be needed they will just be used based on their constraints and no need to type cast (that would be the goal).

Alvarado, Joshua

···

On Apr 24, 2017, at 3:46 PM, Jaden Geller <jaden.geller@gmail.com> wrote:

On Apr 24, 2017, at 2:33 PM, Joshua Alvarado <alvaradojoshua0@gmail.com> wrote:

Hmmm interesting point it would be good to consider this and get to an implementation that helps cover both or just implement the desired feature and in the future add more resiliency.

Alvarado, Joshua

On Apr 24, 2017, at 2:41 PM, Jaden Geller <jaden.geller@gmail.com> wrote:

On Apr 24, 2017, at 1:34 PM, Joshua Alvarado via swift-evolution <swift-evolution@swift.org> wrote:

Well in your case thing one and thing two will be the same type as you are using the same T generic type on both.

To achieve your case you can do an extension on the enum and use two different generics:

enum Thing {
    case thingOne<T>(T)
    case thingTwo<U>(U)
}

extension Thing where T == String, U == Int {
   func handle(thing: Thing) {
    switch thing {
    case thingOne(let s):
        // s is type String
        
    case thingTwo(let i):
        // i is an Int
    }
   }
}

I think it’s important to not that the above example (without generic information carried with the type) should not be able to provide an extension that constrains the type. The problem is that, since we don’t know what `T` and `U` actually are for an arbitrary instance at compile-time, we cannot ensure that `handle` is not called when `T` and `U` don’t match the extension. We’d have to require an implementation of `handle` for every possible type. I guess it _could_ be reasonable to allow such a constrained extension if we already defined `handle` for any `T` and `U`, but this implementation would probably be simple (and equivalent after optimization):

enum Thing {
    case thingOne<T>(T)
    case thingTwo<U>(U)
   
    func handle() {
        switch self {
        case .thingOne<T>(let s):
            if T is String {
                // s is of type String
            } else {
                // default case!
            }
        case .thingTwo<U>(let i):
            if U is Int {
                // i is an Int
            } else {
                // default case!
            }
        }
    }
}

Cheers,
Jaden Geller

This can actually be achieved in Swift 3.1, you can run this in a playground.

This is not quite the same. In the original example, the type information was thrown away after an instance of type `Foo` was constructed.

Here’s an example that wouldn’t work in your model:

enum Whatever {
    case some<T>(T)
}

var x: Whatever = .some(3)
x = .some([1, 2, 3])

If `Whatever` was generic in `T`, then a variable can only store case payloads with that specific type. I guess this could be worked around by introducing a `AnyWhatever` protocol, but still, it’s not an identical feature.

That said, it might be true that this isn’t actually a desirable feature. We ought to definitely consider the implications of it and any alternatives. Thanks for mentioning this, it’s a decent workaround dependent on what you’d like to accomplish!

Cheers,
Jaden Geller

enum Foo<T, U> {
    case bar(obj: T)
    case baz(obj: U)

    func handle() {
        switch self {
        case .bar(obj: let x):
            break
        case .baz(obj: let y):
            break
        }
    }
}

extension Foo where T == String, U == Int {
    func handle() {
        switch self {
        case .bar(obj: let str):
            print(str)
        case .baz(obj: let aNum):
            print(aNum)
        }
    }
}

let foo = Foo<String, Int>.baz(obj: 1)
foo.handle() // prints 1

On Mon, Apr 24, 2017 at 2:15 PM, Kevin Nattinger <swift@nattinger.net> 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?

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?
    case thingTwo<Int>(let i):
        // is it even possible to write an exhaustive switch?
    }
}

On Apr 24, 2017, at 6:57 AM, Joshua Alvarado via swift-evolution <swift-evolution@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! :slight_smile:
Enum with generic cases
Proposal: SE-NNNN
Authors: Joshua Alvarado
Review Manager: TBD
Status: PITCH
During the review process, add the following fields as needed:

Decision Notes: Rationale, Additional Commentary
Bugs: SR-NNNN, SR-MMMM
Previous Revision: 1
Previous Proposal: SE-XXXX
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

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.

Proposed solution

The generic is to be casted on the case of the enum and not on the enum itself.

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")
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.

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

--
Joshua Alvarado
alvaradojoshua0@gmail.com
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Joshua Alvarado
alvaradojoshua0@gmail.com
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Okay I will resubmit after post Swift 4.0 scope is discussed. Thanks!

Alvarado, Joshua

···

On Apr 25, 2017, at 11:08 AM, Douglas Gregor <dgregor@apple.com> wrote:

On Apr 24, 2017, at 12:57 PM, Joshua Alvarado <alvaradojoshua0@gmail.com> wrote:

When would be a good time to resubmit this proposal for discussion?

Sometime in the summer we’re going to start talking about the scope of Swift post-4.0.

Or can I still proceed with the review and it possibly gets deferred but at least be put in the pipeline (if deferred)?

We try not to let the review process get *too* far ahead of the actual implementation, because detailed design review for acceptance is itself a time-consuming process.

  - Doug

On Mon, Apr 24, 2017 at 9:07 AM, Douglas Gregor <dgregor@apple.com> wrote:

On Apr 24, 2017, at 7:23 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

/me Pushes implementation detail related concerns out of head

+1

I want this feature but I seriously doubt that it is feasible at the moment. There are so many 'more pressing' features that, even if this were accepted now, it wouldn't be implemented it in time for Swift 4.

Out of scope for Swift 4, certainly. It may not look like it, but this is a fairly large feature. I suggest reading up on generalized algebraic data types (GADTs), which is the more programming-language-theoretical name for what you’re describing here.

  - Doug

That said, I would love to be incorrect.

On Mon, Apr 24, 2017 at 9:57 AM, Joshua Alvarado via swift-evolution <swift-evolution@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! :slight_smile:
Enum with generic cases
Proposal: SE-NNNN
Authors: Joshua Alvarado
Review Manager: TBD
Status: PITCH
During the review process, add the following fields as needed:

Decision Notes: Rationale, Additional Commentary
Bugs: SR-NNNN, SR-MMMM
Previous Revision: 1
Previous Proposal: SE-XXXX
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

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.

Proposed solution

The generic is to be casted on the case of the enum and not on the enum itself.

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")
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.

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

--
Joshua Alvarado
alvaradojoshua0@gmail.com

_______________________________________________
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

--
Joshua Alvarado
alvaradojoshua0@gmail.com