Making Error sub-enums Equatable


(Rick M) #1

Seriously, I've been googling this for a half-hour, and I can't find an answer (everything that comes up is for ErrorType, absolutely nothing for Error).

I have an enum:

enum MyErrors : Error
{
    case one(String)
    case two
    case three(String)
}

let a: MyErrors = .one("foo")
let b = .two
let c = .towo

I want to compare them with ==, and I don't care about the associated types. I can't for the life of me figure out how without an exhaustive switch statement in a == definition. Is that the only way?

···

--
Rick Mann
rmann@latencyzero.com


(Rien) #2

I’d love to know if there is a better way, but a ‘switch’ or 'if case' is the only way I know.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl - A server for websites build in Swift

···

On 08 May 2017, at 11:01, Rick Mann via swift-users <swift-users@swift.org> wrote:

Seriously, I've been googling this for a half-hour, and I can't find an answer (everything that comes up is for ErrorType, absolutely nothing for Error).

I have an enum:

enum MyErrors : Error
{
   case one(String)
   case two
   case three(String)
}

let a: MyErrors = .one("foo")
let b = .two
let c = .towo

I want to compare them with ==, and I don't care about the associated types. I can't for the life of me figure out how without an exhaustive switch statement in a == definition. Is that the only way?

--
Rick Mann
rmann@latencyzero.com

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


(Brent Royal-Gordon) #3

Yes, the correct way to compare two enums is with a `switch` statement.

The good news is, Swift's `switch` statement is good enough that these aren't terribly difficult to write. My preferred pattern (given your "ignore the associated type" semantic) is:

  extension MyErrors: Equatable {
    static func == (lhs: MyErrors, rhs: MyErrors) -> Bool {
      switch (lhs, rhs) {
      case (.one, .one):
        return true
      case (.two, .two):
        return true
      case (.three, .three):
        return true
      case (.one, _), (.two, _), (.three, _):
        return false
      }
    }
  }

You do it this way instead of using `default:` so that, if you add another case later, it won't just get matched by the `default:` and always return `false`.

(P.S. I would suggest using a name like `MyError`, not `MyErrors`. A given instance of `MyError` only represents one of the errors, not several of them.)

···

On May 8, 2017, at 2:01 AM, Rick Mann via swift-users <swift-users@swift.org> wrote:

Seriously, I've been googling this for a half-hour, and I can't find an answer (everything that comes up is for ErrorType, absolutely nothing for Error).

I have an enum:

enum MyErrors : Error
{
   case one(String)
   case two
   case three(String)
}

let a: MyErrors = .one("foo")
let b = .two
let c = .towo

I want to compare them with ==, and I don't care about the associated types. I can't for the life of me figure out how without an exhaustive switch statement in a == definition. Is that the only way?

--
Brent Royal-Gordon
Architechies


(Zhao Xin) #4

I think you'd better define your own operator, maybe `=~` or something
else. As `==` has already meant something in enum.

Zhaoxin

···

On Mon, May 8, 2017 at 5:07 PM, Rien via swift-users <swift-users@swift.org> wrote:

I’d love to know if there is a better way, but a ‘switch’ or 'if case' is
the only way I know.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl - A server for websites build in Swift

> On 08 May 2017, at 11:01, Rick Mann via swift-users < > swift-users@swift.org> wrote:
>
> Seriously, I've been googling this for a half-hour, and I can't find an
answer (everything that comes up is for ErrorType, absolutely nothing for
Error).
>
> I have an enum:
>
> enum MyErrors : Error
> {
> case one(String)
> case two
> case three(String)
> }
>
> let a: MyErrors = .one("foo")
> let b = .two
> let c = .towo
>
> I want to compare them with ==, and I don't care about the associated
types. I can't for the life of me figure out how without an exhaustive
switch statement in a == definition. Is that the only way?
>
> --
> Rick Mann
> rmann@latencyzero.com
>
>
> _______________________________________________
> swift-users mailing list
> swift-users@swift.org
> https://lists.swift.org/mailman/listinfo/swift-users

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


(Rick M) #5

Seriously, I've been googling this for a half-hour, and I can't find an answer (everything that comes up is for ErrorType, absolutely nothing for Error).

I have an enum:

enum MyErrors : Error
{
  case one(String)
  case two
  case three(String)
}

let a: MyErrors = .one("foo")
let b = .two
let c = .towo

I want to compare them with ==, and I don't care about the associated types. I can't for the life of me figure out how without an exhaustive switch statement in a == definition. Is that the only way?

Yes, the correct way to compare two enums is with a `switch` statement.

The good news is, Swift's `switch` statement is good enough that these aren't terribly difficult to write. My preferred pattern (given your "ignore the associated type" semantic) is:

  extension MyErrors: Equatable {
    static func == (lhs: MyErrors, rhs: MyErrors) -> Bool {
      switch (lhs, rhs) {
      case (.one, .one):
        return true
      case (.two, .two):
        return true
      case (.three, .three):
        return true
      case (.one, _), (.two, _), (.three, _):
        return false
      }
    }
  }

You do it this way instead of using `default:` so that, if you add another case later, it won't just get matched by the `default:` and always return `false`.

This seems so obvious that I feel like it should be provided by the language by default. I suppose you can make it even more compact with

    case (.one, .one),
         (.two, .two),
         (.three, .three):
  return true

Maybe swift could provide a 'case==()' so one needn't write this (I can see it getting quite tedious and error-prone, should one forget to update the equality after adding additional cases).

(P.S. I would suggest using a name like `MyError`, not `MyErrors`. A given instance of `MyError` only represents one of the errors, not several of them.)

I did in my actual code. This was just quickly typing the email and hiding the real error name.

···

On May 10, 2017, at 01:23 , Brent Royal-Gordon <brent@architechies.com> wrote:

On May 8, 2017, at 2:01 AM, Rick Mann via swift-users <swift-users@swift.org> wrote:

--
Rick Mann
rmann@latencyzero.com


(Quinn “The Eskimo!”) #6

This is the bit that worries me. The docs for `Equatable` are very clear that it implies /substitutability/, which is not the case if you ignore the associated values.

<https://developer.apple.com/reference/swift/equatable>

Share and Enjoy

···

On 10 May 2017, at 09:23, Brent Royal-Gordon via swift-users <swift-users@swift.org> wrote:

(given your "ignore the associated type" semantic)

--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware


(Brent Royal-Gordon) #7

There's been some talk on swift-evolution about synthesizing an `Equatable` implementation for enums with associated types, but that would not implement your slightly weird equality semantic—it would compare the associated types, which isn't what you want. The same would undoubtedly be true for any other syntactic sugar we might provide. So I wouldn't hold your breath waiting for a better way to implement it than this.

···

On May 10, 2017, at 2:05 AM, Rick Mann <rmann@latencyzero.com> wrote:

This seems so obvious that I feel like it should be provided by the language by default. I suppose you can make it even more compact with

   case (.one, .one),
        (.two, .two),
        (.three, .three):
  return true

Maybe swift could provide a 'case==()' so one needn't write this (I can see it getting quite tedious and error-prone, should one forget to update the equality after adding additional cases).

--
Brent Royal-Gordon
Architechies


(Ronaldo Faria Lima) #8

Rigorously, one should avoid to force the purpose of a given feature of a language or library construct. If you use Equatable, you must take into account the associated values for the sake of semantics.

However, you are free to create your own infix operator that could allow you compare your enum cases the way you want. It is important, however, to be sure that it will have a semantic clear enough so someone reading your code could understand it.

Regards,

Ronaldo “Clocksmith” Lima

···

Em 10 de mai de 2017, à(s) 06:16, Quinn The Eskimo! via swift-users <swift-users@swift.org> escreveu:

On 10 May 2017, at 09:23, Brent Royal-Gordon via swift-users <swift-users@swift.org> wrote:

(given your "ignore the associated type" semantic)

This is the bit that worries me. The docs for `Equatable` are very clear that it implies /substitutability/, which is not the case if you ignore the associated values.

<https://developer.apple.com/reference/swift/equatable>

Share and Enjoy
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

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