[swift-evolution] [Pitch] Enum with generic cases
Jaden Geller
jaden.geller at gmail.com
Mon Apr 24 17:16:40 CDT 2017
> On Apr 24, 2017, at 2:38 PM, Kevin Nattinger <swift at nattinger.net> wrote:
>
>>
>> How can I improve your understanding?
>>
>
>
> Given the enum I was using earlier:
>
> enum Thing {
> case thingOne<T>(T)
> case thingTwo<T>(T)
> }
>
> - Write a function that takes a thingOne<String> or thingTwo<Int> but nothing else.
This isn’t possible since generic types introduced on cases are erased in the type of `Thing`.
We can actually already achieve what you want by moving the generics onto the type itself, and this is already possible in Swift! No new features are necessary.
```
enum Thing<T1, T2> {
case thingOne(T1)
case thingTwo(T2)
}
func test(_ x: Thing<String, Int>) {
switch x {
case .thingOne(let s):
print("The string has value \(s)!")
case .thingTwo(let i):
print("The int has value \(i)!")
}
}
```
> - Declare a variable that can hold a thingOne<String> or thingTwo<Int> but nothing else.
With our new definition, we can write:
```
var x: Thing<String, Int>
```
This variable can be initialized with a `thingOne` containing a `String` payload or a `thingTwo` containing an `Int` payload.
> Or explain why something that is widely used, trivial to write, and necessary for type safety should be removed from the language.
As I just explained, what you desire is *already possible* in the language. This new features does not address your use case. Instead, this feature allows for type erasure of enum cases. This is pretty similar to storing an existential as a payload. I’m not even sure if it’s sufficiently different to justify its addition to the language.
The original non-generic definition of `Thing` with generic cases is essentially equivalent to storying `Any` as the case payloads. We can make this more useful by using protocols to constrain the payload type.
I think this proposal doesn’t really add any useful new capabilities without the ability to constraint the return type. For example, these are nearly equivalent:
```
enum Key {
case hashable<T: Hashable>(T)
case any<T>(T)
}
```
vs.
```
enum Key {
case hashable(AnyHashable)
case any(Any)
}
```
Both of these definitions are nearly equivalent except the former introduces a new generic variable in scope when you match on a constrained case. Is this useful? I’m not sure. Is it breaking type safety? Absolutely not!! If I match the `hashable` case in a switch statement, it’s very similar to if I had written a function with the following signature:
```
func matchHashable<T: Hashable>(_ x: T) { ... }
```
Because of this, no type safety is lost in the switch statement.
—
I think a more interesting version of this proposal would be as follows: Allow where constraints and explicit return type annotations on case declarations. This is very similar to what a GADT <https://en.wikibooks.org/wiki/Haskell/GADT> allows you to do. Check out how cool this example is—and 110% type-safe!
Below is an example of a data structure representing a computation. We can inspect is since it is just data, but we can also evaluate it to see the result.
```
enum Expression<T> {
case value(T)
case isEqual(T, T) -> Expression<Bool> where T: Equatable
case plus(T, T) -> Expression<T> where T: Numeric
case and(Bool, Bool) -> Expression<Bool>
case not(Bool) -> Expression<Bool>
// etc.
func evaluate() -> T {
switch self {
case .value(let x):
return x
case .isEqual(let lhs, let rhs):
// We know that T: Equatable, so we can use `==`
return lhs == rhs
case .plus(let lhs, let rhs):
// We know that T: Numeric, so we can use `+`
return lhs + rhs
case .and(let lhs, let rhs):
return lhs && rhs
case .not(let x):
return !x
}
}
}
let x: Expression<Int32> = .plus(.value(3), .value(10))
let y: Expression<Int32> = .plus(.value(7), .value(6))
let z: Expression<Bool> = .isEqual(x, y)
print(z.evaluate()) // -> true
```
Notice that it is impossible to construct an `Expression` of a nonsensical type. Try to do this with the enums we have in Swift today. You’ll have to do some force casting of types. Not pretty.
—
I hope I was able to clarify some things for you. Let me know if you have any other questions.
Cheers,
Jaden Geller
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170424/f6a41b4e/attachment.html>
More information about the swift-evolution
mailing list