4
data CouldBe a = Is a | Lost deriving (Show, Ord)

instance Eq (CouldBe m) where
  Is x == Is y = x == y 
  Lost == Lost = True 
  _ == _ = False 

Gives an error: No instance for (Eq m) arising from a use of ‘==’ So:

instance (Eq m) => Eq (CouldBe m) where
  Is x == Is y = x == y 
  Lost == Lost = True 
  _ == _ = False 

Works fine (at least I start understanding the errors), but why do I need that constraint? I am trying to learn, so the 'why' is very important to me.

1 Answer 1

6

Your original definition said that CouldBe m was an instance of Eq for any type m, even one that doesn't have an Eq instance. But if that's true, you have to find some way of defining Is x == Is y without using x == y (since you haven't required m to have an Eq instance, x == y isn't necessarily defined.)

As a specific example, it prevents you from writing something like

Is (+3) == Is (* 5)  -- (+3) == (*5) is undefined

Adding the constraint ensures you can compare two CouldBe values only if the wrapped type can also be compared.


A "valid", but trivial, instance without adding the constraint:

instance Eq (CouldBe m) where
  Is x == Is y = True
  Lost == Lost = True
  _ == _ = False

Two CouldBe m values are equal as long as they share the same data constructor, regardless of the wrapped value. No attempt is made to use x or y at all, so their types can be unconstrained.

"Valid" is in quotes because this definition could be in violation of the substitutivity law, defined at http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Eq.html. Suppose you had a function that could pull apart a CouldBe value:

couldbe :: b -> (a -> b) -> CouldBe a -> b
couldBe x _ Lost = x
couldBe _ f (Is x) = f x

The violation occurs because Is 3 == Is 5 would be true, but let f = couldbe 0 id. Then f (Is 3) == f (Is 5) evaluates to 3 == 5 which is false.

Whether it's actually a violation or not depends on the existence of a function like couldbe that can see "inside" a CouldBe value.

Sign up to request clarification or add additional context in comments.

3 Comments

Brilliant. Understandable and noted! Thx
I think if you're going to call the trivial instance "valid" you should point out that this only follows the Eq substitutivity law if you don't provide a way for clients to observe the value inside Is. More likely, those values are exposed, and so the trivial instance technically compiles but doesn't behave like an Eq instance should.
Thanks; I don't think I ever noticed the substitutivity law before.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.