Equatability for enums with associated values

When using enums with associated values, it's often necessary to check for
equality between two enum objects in some way. That can lead to boilerplate
code like this:

enum Option {
    case foo(String)
    case bar(Int)
case zip
}

func ==(lhs: Option, rhs: Option) -> Bool {
    switch (lhs, rhs) {
    case (.foo(let a), .foo(let b)) where a == b: return true
    case (.bar(let a), .bar(let b)) where a == b: return true
    case (.zip, .zip): return true
    default: return false
    }
}

..which results in code duplication and opens the door to potential logic
errors.

Instead, what if enums with associated values were automatically Equatable
when all their associated values were Equatable? That would remove the need
for such boilerplate code.

The Swift language guide states that custom classes and structs don't
receive a default implementation of the == operator because the compiler
can't guess what "equality" means for them. However, I think this could
make sense for enums. An enum case, even with associated values, seems
closer to a value itself than an object with logic and state.

I'd be interested to hear any thoughts on this. Would this be a beneficial
addition?

+1, with the caveat that you should still have to explicitly mark it Equatable.

I think the “when all their associated values were Equatable” is the technical issue holding this type of thing up. The ability to spell that type of thing is on the generics roadmap, but I don’t know when it will actually happen. There seem to be a lot of things on hold because of it.

Thanks,
Jon

···

On Jan 13, 2017, at 11:51 AM, Adam Shin via swift-evolution <swift-evolution@swift.org> wrote:

When using enums with associated values, it's often necessary to check for equality between two enum objects in some way. That can lead to boilerplate code like this:

enum Option {
    case foo(String)
    case bar(Int)
  case zip
}

func ==(lhs: Option, rhs: Option) -> Bool {
    switch (lhs, rhs) {
    case (.foo(let a), .foo(let b)) where a == b: return true
    case (.bar(let a), .bar(let b)) where a == b: return true
    case (.zip, .zip): return true
    default: return false
    }
}

..which results in code duplication and opens the door to potential logic errors.

Instead, what if enums with associated values were automatically Equatable when all their associated values were Equatable? That would remove the need for such boilerplate code.

The Swift language guide states that custom classes and structs don't receive a default implementation of the == operator because the compiler can't guess what "equality" means for them. However, I think this could make sense for enums. An enum case, even with associated values, seems closer to a value itself than an object with logic and state.

I'd be interested to hear any thoughts on this. Would this be a beneficial addition?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think the “when all their associated values were Equatable” is the technical issue holding this type of thing up. The ability to spell that type of thing is on the generics roadmap, but I don’t know when it will actually happen. There seem to be a lot of things on hold because of it.

The proposal for conditional conformances was accepted. However, right now the generics feature being worked on is recursive conformances, together with a large overall cleanup of the generics implementation to fix bugs and improve correctness. Conditional conformances will come at some point after that.

Slava

···

On Jan 13, 2017, at 12:14 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Thanks,
Jon

On Jan 13, 2017, at 11:51 AM, Adam Shin via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

When using enums with associated values, it's often necessary to check for equality between two enum objects in some way. That can lead to boilerplate code like this:

enum Option {
    case foo(String)
    case bar(Int)
  case zip
}

func ==(lhs: Option, rhs: Option) -> Bool {
    switch (lhs, rhs) {
    case (.foo(let a), .foo(let b)) where a == b: return true
    case (.bar(let a), .bar(let b)) where a == b: return true
    case (.zip, .zip): return true
    default: return false
    }
}

..which results in code duplication and opens the door to potential logic errors.

Instead, what if enums with associated values were automatically Equatable when all their associated values were Equatable? That would remove the need for such boilerplate code.

The Swift language guide states that custom classes and structs don't receive a default implementation of the == operator because the compiler can't guess what "equality" means for them. However, I think this could make sense for enums. An enum case, even with associated values, seems closer to a value itself than an object with logic and state.

I'd be interested to hear any thoughts on this. Would this be a beneficial addition?
_______________________________________________
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

A million yes-es please.

l8r
Sean

···

On Jan 13, 2017, at 1:51 PM, Adam Shin via swift-evolution <swift-evolution@swift.org> wrote:

When using enums with associated values, it's often necessary to check for equality between two enum objects in some way. That can lead to boilerplate code like this:

enum Option {
    case foo(String)
    case bar(Int)
  case zip
}

func ==(lhs: Option, rhs: Option) -> Bool {
    switch (lhs, rhs) {
    case (.foo(let a), .foo(let b)) where a == b: return true
    case (.bar(let a), .bar(let b)) where a == b: return true
    case (.zip, .zip): return true
    default: return false
    }
}

..which results in code duplication and opens the door to potential logic errors.

Instead, what if enums with associated values were automatically Equatable when all their associated values were Equatable? That would remove the need for such boilerplate code.

The Swift language guide states that custom classes and structs don't receive a default implementation of the == operator because the compiler can't guess what "equality" means for them. However, I think this could make sense for enums. An enum case, even with associated values, seems closer to a value itself than an object with logic and state.

I'd be interested to hear any thoughts on this. Would this be a beneficial addition?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think it makes more sense to come up with some syntax for reducing that kind of boilerplate code in general. In "pseudo-english", I'd write the == function as "if lhs and rhs are the same case && their associated values are the equal, return true, else return false".

What about doing something with the reflection system? Isn't that supposed to get overhauled for Swift 4? I'm not sure what the performance implications would be, though.

- Dave Sweeris

···

Sent from my iPhone

On Jan 13, 2017, at 13:51, Adam Shin via swift-evolution <swift-evolution@swift.org> wrote:

When using enums with associated values, it's often necessary to check for equality between two enum objects in some way. That can lead to boilerplate code like this:

enum Option {
    case foo(String)
    case bar(Int)
  case zip
}

func ==(lhs: Option, rhs: Option) -> Bool {
    switch (lhs, rhs) {
    case (.foo(let a), .foo(let b)) where a == b: return true
    case (.bar(let a), .bar(let b)) where a == b: return true
    case (.zip, .zip): return true
    default: return false
    }
}

..which results in code duplication and opens the door to potential logic errors.

Instead, what if enums with associated values were automatically Equatable when all their associated values were Equatable? That would remove the need for such boilerplate code.

The Swift language guide states that custom classes and structs don't receive a default implementation of the == operator because the compiler can't guess what "equality" means for them. However, I think this could make sense for enums. An enum case, even with associated values, seems closer to a value itself than an object with logic and state.

I'd be interested to hear any thoughts on this. Would this be a beneficial addition?

This is a request that comes up frequently; I wrote an early draft proposal
for it several months ago, with it covering all value types, not just
enums, and also including Hashable as well as Equatable:

Since it's purely additive, it won't get much attention until phase 2 at
the earliest, but I'd like to revisit it then. Most of the discussion
before revolved around to what degree users should have to opt-in or
opt-out of this functionality.

Re: generics, I don't think there's anything on the roadmap that goes into
such deep detail as "if every associated value of every enum case is
Equatable, then X is also Equatable", and I don't think there would be a
clean way to express that with the generics syntax. This would probably
have to be something that's baked into the compiler to synthesize == and
hashValue for types that satisfy the constraints.

···

On Fri, Jan 13, 2017 at 12:31 PM David Sweeris via swift-evolution < swift-evolution@swift.org> wrote:

Sent from my iPhone
On Jan 13, 2017, at 13:51, Adam Shin via swift-evolution < > swift-evolution@swift.org> wrote:

When using enums with associated values, it's often necessary to check for
equality between two enum objects in some way. That can lead to boilerplate
code like this:

enum Option {
    case foo(String)
    case bar(Int)
case zip
}

func ==(lhs: Option, rhs: Option) -> Bool {
    switch (lhs, rhs) {
    case (.foo(let a), .foo(let b)) where a == b: return true
    case (.bar(let a), .bar(let b)) where a == b: return true
    case (.zip, .zip): return true
    default: return false
    }
}

..which results in code duplication and opens the door to potential logic
errors.

Instead, what if enums with associated values were automatically Equatable
when all their associated values were Equatable? That would remove the need
for such boilerplate code.

The Swift language guide states that custom classes and structs don't
receive a default implementation of the == operator because the compiler
can't guess what "equality" means for them. However, I think this could
make sense for enums. An enum case, even with associated values, seems
closer to a value itself than an object with logic and state.

I'd be interested to hear any thoughts on this. Would this be a beneficial
addition?

I think it makes more sense to come up with some syntax for reducing that
kind of boilerplate code in general. In "pseudo-english", I'd write the ==
function as "if lhs and rhs are the same case && their associated values
are the equal, return true, else return false".

What about doing something with the reflection system? Isn't that supposed
to get overhauled for Swift 4? I'm not sure what the performance
implications would be, though.

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

That seems pretty close to Rust’s derive. Why not invent a similar syntax
in Swift? Otherwise it will make us look through all the sources to make
sure deriving is used.

enum Option: @derive Equatable {
    ...
}

Also, we can get better looking compilation errors, like:

ERROR at line 1, col 14: could not derive Equatable for Option
enum Option: @derive Equatable {
             ^~~~~~~~~~~~~~~~~

My feeling on this is that it feels strange to treat compiler-provided default implementations as different from library-provided default implementations. If the library provides a protocol extension with an appropriate default implementation for your type, you get it without any work on your part. While the Equatable/Hashable/Comparable conformance would most practically compiler generated today, you could imagine a far future version of Swift having sufficiently powerful generic type traits to do this in the library.

-Joe

···

On Jan 13, 2017, at 1:10 PM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:

That seems pretty close to Rust’s derive. Why not invent a similar syntax in Swift? Otherwise it will make us look through all the sources to make sure deriving is used.

enum Option: @derive Equatable {
    ...
}

This is a request that comes up frequently; I wrote an early draft proposal for it several months ago, with it covering all value types, not just enums, and also including Hashable as well as Equatable:

NNNN-value-equatability-and-hashability.md · GitHub

Since it's purely additive, it won't get much attention until phase 2 at the earliest, but I'd like to revisit it then. Most of the discussion before revolved around to what degree users should have to opt-in or opt-out of this functionality.

I think if someone were to implement this though, we would be happy to merge it in. The code for deriving Equatable and Hashable conformances is in these files:

lib/Sema/CodeSynthesis.cpp
lib/Sema/DerivedConformances.cpp
lib/Sema/DerivedConformanceEquatableHashable.cpp

I agree it should be opt-in though (I would also argue that even the no-payload enum behavior should be opt-in, but that ship has sailed).

Re: generics, I don't think there's anything on the roadmap that goes into such deep detail as "if every associated value of every enum case is Equatable, then X is also Equatable", and I don't think there would be a clean way to express that with the generics syntax. This would probably have to be something that's baked into the compiler to synthesize == and hashValue for types that satisfy the constraints.

The generics feature people are thinking of is conditional conformances:

extension Array : Equatable where Element == Equatable { … }

However it does not solve the problem for enums with associated values because as you said there’s no way to quantify over ‘all associated values’ in the language, and this is not planned.

Slava

···

On Jan 13, 2017, at 1:15 PM, Tony Allevato via swift-evolution <swift-evolution@swift.org> wrote:

On Fri, Jan 13, 2017 at 12:31 PM David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPhone
On Jan 13, 2017, at 13:51, Adam Shin via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

When using enums with associated values, it's often necessary to check for equality between two enum objects in some way. That can lead to boilerplate code like this:

enum Option {
    case foo(String)
    case bar(Int)
  case zip
}

func ==(lhs: Option, rhs: Option) -> Bool {
    switch (lhs, rhs) {
    case (.foo(let a), .foo(let b)) where a == b: return true
    case (.bar(let a), .bar(let b)) where a == b: return true
    case (.zip, .zip): return true
    default: return false
    }
}

..which results in code duplication and opens the door to potential logic errors.

Instead, what if enums with associated values were automatically Equatable when all their associated values were Equatable? That would remove the need for such boilerplate code.

The Swift language guide states that custom classes and structs don't receive a default implementation of the == operator because the compiler can't guess what "equality" means for them. However, I think this could make sense for enums. An enum case, even with associated values, seems closer to a value itself than an object with logic and state.

I'd be interested to hear any thoughts on this. Would this be a beneficial addition?

I think it makes more sense to come up with some syntax for reducing that kind of boilerplate code in general. In "pseudo-english", I'd write the == function as "if lhs and rhs are the same case && their associated values are the equal, return true, else return false".

What about doing something with the reflection system? Isn't that supposed to get overhauled for Swift 4? I'm not sure what the performance implications would be, though.

- Dave Sweeris
_______________________________________________
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

That seems pretty close to Rust’s derive. Why not invent a similar syntax in Swift? Otherwise it will make us look through all the sources to make sure deriving is used.

enum Option: @derive Equatable {
    ...
}
Also, we can get better looking compilation errors, like:

ERROR at line 1, col 14: could not derive Equatable for Option
enum Option: @derive Equatable {
             ^~~~~~~~~~~~~~~~~

I think that idea came up once before... can't remember where, though, or what we thought of it at the time.

As far as reducing enum boilerplate, what about borrowing the generic system's syntax and looking at it from the switch's PoV?
func == (lhs: MyEnum, rhs: MyEnum) -> Bool {
    switch <c is MyEnum> (lhs, rhs) {
    case (c(let lVal), c(let rVal)): // both lhs and rhs are "c" and the same case
        return lVal == rVal //syntax error if `==` isn't defined for the associated value types of every case
    default: return false
    }
}

I think initially, we would like to implement deriving these witnesses directly in the compiler, instead of trying to come up with a metaprogramming syntax for them.

Slava

···

On Jan 13, 2017, at 2:30 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:
On Jan 13, 2017, at 15:10, Anton Zhilin via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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

I think that idea came up once before... can't remember where, though, or what we thought of it at the time.

As far as reducing enum boilerplate, what about borrowing the generic system's syntax and looking at it from the switch's PoV?
func == (lhs: MyEnum, rhs: MyEnum) -> Bool {
    switch <c is MyEnum> (lhs, rhs) {
    case (c(let lVal), c(let rVal)): // both lhs and rhs are "c" and the same case
        return lVal == rVal //syntax error if `==` isn't defined for the associated value types of every case
    default: return false
    }
}

- Dave Sweeris

···

On Jan 13, 2017, at 15:10, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:

That seems pretty close to Rust’s derive. Why not invent a similar syntax in Swift? Otherwise it will make us look through all the sources to make sure deriving is used.

enum Option: @derive Equatable {
    ...
}
Also, we can get better looking compilation errors, like:

ERROR at line 1, col 14: could not derive Equatable for Option
enum Option: @derive Equatable {
             ^~~~~~~~~~~~~~~~~

I think it is better to create a syntax for getting the associated values
and then comparing them.

enum Option {
case foo(String)
case bar(Int)
case zip
}

let op = Option.foo("hello")
let bb = Option.foo("world")

// proposed tuple-like syntax

op.foo.0 // returns Optional("hello")

// then we can compare values directly

if op.foo.0 == bb.foo.0 {
// ...
}

···

On Fri, Jan 13, 2017 at 5:44 PM Slava Pestov via swift-evolution < swift-evolution@swift.org> wrote:

On Jan 13, 2017, at 2:30 PM, David Sweeris via swift-evolution < > swift-evolution@swift.org> wrote:

On Jan 13, 2017, at 15:10, Anton Zhilin via swift-evolution < > swift-evolution@swift.org> wrote:

That seems pretty close to Rust’s derive. Why not invent a similar syntax
in Swift? Otherwise it will make us look through all the sources to make
sure deriving is used.

enum Option: @derive Equatable {
    ...
}

Also, we can get better looking compilation errors, like:

ERROR at line 1, col 14: could not derive Equatable for Option
enum Option: @derive Equatable {
             ^~~~~~~~~~~~~~~~~

I think that idea came up once before... can't remember where, though, or
what we thought of it at the time.

As far as reducing enum boilerplate, what about borrowing the generic
system's syntax and looking at it from the switch's PoV?
func == (lhs: MyEnum, rhs: MyEnum) -> Bool {
    switch <c is MyEnum> (lhs, rhs) {
    case (c(let lVal), c(let rVal)): // both lhs and rhs are "c" and the
same case
        return lVal == rVal //syntax error if `==` isn't defined for the
associated value types of every case
    default: return false
    }
}

I think initially, we would like to implement deriving these witnesses
directly in the compiler, instead of trying to come up with a
metaprogramming syntax for them.

Slava

- Dave Sweeris
_______________________________________________
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

Such a proposed syntax doesn't solve the general problem though, which is
that comparing two enum values requires enumerating all of the cases to
test whether they are (1) the same and (2) have the same associated values,
if any. The desire here is to get rid of the boilerplate that users must
write to implement simple equality (and hashability, in the case of my
proposal draft), similarly to how enums without associated values already
get Equatable and Hashable for free.

···

On Fri, Jan 13, 2017 at 3:37 PM Derrick Ho via swift-evolution < swift-evolution@swift.org> wrote:

I think it is better to create a syntax for getting the associated values
and then comparing them.

enum Option {
case foo(String)
case bar(Int)
case zip
}

let op = Option.foo("hello")
let bb = Option.foo("world")

// proposed tuple-like syntax

op.foo.0 // returns Optional("hello")

// then we can compare values directly

if op.foo.0 == bb.foo.0 {
// ...
}

On Fri, Jan 13, 2017 at 5:44 PM Slava Pestov via swift-evolution < > swift-evolution@swift.org> wrote:

On Jan 13, 2017, at 2:30 PM, David Sweeris via swift-evolution < > swift-evolution@swift.org> wrote:

On Jan 13, 2017, at 15:10, Anton Zhilin via swift-evolution < > swift-evolution@swift.org> wrote:

That seems pretty close to Rust’s derive. Why not invent a similar syntax
in Swift? Otherwise it will make us look through all the sources to make
sure deriving is used.

enum Option: @derive Equatable {
    ...
}

Also, we can get better looking compilation errors, like:

ERROR at line 1, col 14: could not derive Equatable for Option
enum Option: @derive Equatable {
             ^~~~~~~~~~~~~~~~~

I think that idea came up once before... can't remember where, though, or
what we thought of it at the time.

As far as reducing enum boilerplate, what about borrowing the generic
system's syntax and looking at it from the switch's PoV?
func == (lhs: MyEnum, rhs: MyEnum) -> Bool {
    switch <c is MyEnum> (lhs, rhs) {
    case (c(let lVal), c(let rVal)): // both lhs and rhs are "c" and the
same case
        return lVal == rVal //syntax error if `==` isn't defined for the
associated value types of every case
    default: return false
    }
}

I think initially, we would like to implement deriving these witnesses
directly in the compiler, instead of trying to come up with a
metaprogramming syntax for them.

Slava

- Dave Sweeris
_______________________________________________
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

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

It would be helpful for synthesised RawRep conformance. The inheritance-like syntax we have now is awful - it makes people think that RawRepresentable is some kind of magic protocol that will allow special compiler jango to happen.

You could see why they think that. This looks very much like the enum is going to *be* an Int32:

enum Something: Int32 {
    case oneThing = 36
    case anotherThing = 42
}

This is also one of the icky parts to allowing tuples of integer/string literals (something people ask for quite a lot). It would look like you’re deriving your enum from a non-nominal type:

enum Something: (Int32, Int32) {
    case oneThing = (3, 12)
    case anotherThing = (5, 9)
}

Getting back to the topic: we should do this. We do it for plain enums. We do it for tuples of Equatables. No reason we can’t do it for enums whose payloads are Equatable tuples.

As for the broader picture on enums, my opinion is that it could be addressed together with extending tuples and allowing them to conform to protocols. In both cases we are expressing a conditional conformance for all (tuples/enums) whose payload types match a set of constraints to conform to a protocol. They would both require syntax to bind flexible numbers of (tuple elements/enum cases) using some type constraints.

- Karl

···

On 13 Jan 2017, at 23:02, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 13, 2017, at 1:10 PM, Anton Zhilin via swift-evolution <swift-evolution@swift.org> wrote:

That seems pretty close to Rust’s derive. Why not invent a similar syntax in Swift? Otherwise it will make us look through all the sources to make sure deriving is used.

enum Option: @derive Equatable {
   ...
}

My feeling on this is that it feels strange to treat compiler-provided default implementations as different from library-provided default implementations. If the library provides a protocol extension with an appropriate default implementation for your type, you get it without any work on your part. While the Equatable/Hashable/Comparable conformance would most practically compiler generated today, you could imagine a far future version of Swift having sufficiently powerful generic type traits to do this in the library.

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

Tuples of Equatables are not themselves Equatable just yet, though that would be a reasonable thing to add. They just have a bunch of `==` overloads.

-Joe

···

On Jan 16, 2017, at 10:28 AM, Karl Wagner <razielim@gmail.com> wrote:

On 13 Jan 2017, at 23:02, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 13, 2017, at 1:10 PM, Anton Zhilin via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

That seems pretty close to Rust’s derive. Why not invent a similar syntax in Swift? Otherwise it will make us look through all the sources to make sure deriving is used.

enum Option: @derive Equatable {
   ...
}

My feeling on this is that it feels strange to treat compiler-provided default implementations as different from library-provided default implementations. If the library provides a protocol extension with an appropriate default implementation for your type, you get it without any work on your part. While the Equatable/Hashable/Comparable conformance would most practically compiler generated today, you could imagine a far future version of Swift having sufficiently powerful generic type traits to do this in the library.

-Joe
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

It would be helpful for synthesised RawRep conformance. The inheritance-like syntax we have now is awful - it makes people think that RawRepresentable is some kind of magic protocol that will allow special compiler jango to happen.

You could see why they think that. This looks very much like the enum is going to *be* an Int32:

enum Something: Int32 {
    case oneThing = 36
    case anotherThing = 42
}

This is also one of the icky parts to allowing tuples of integer/string literals (something people ask for quite a lot). It would look like you’re deriving your enum from a non-nominal type:

enum Something: (Int32, Int32) {
    case oneThing = (3, 12)
    case anotherThing = (5, 9)
}

Getting back to the topic: we should do this. We do it for plain enums. We do it for tuples of Equatables.