Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell instance of typeclass definition with type constructor

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.

like image 542
Madderote Avatar asked Mar 07 '19 16:03

Madderote


People also ask

How do you define Typeclass in Haskell?

What's a typeclass in Haskell? A typeclass defines a set of methods that is shared across multiple types. For a type to belong to a typeclass, it needs to implement the methods of that typeclass. These implementations are ad-hoc: methods can have different implementations for different types.

What is a type constructor Haskell?

In a data declaration, a type constructor is the thing on the left hand side of the equals sign. The data constructor(s) are the things on the right hand side of the equals sign. You use type constructors where a type is expected, and you use data constructors where a value is expected.

What is an instance in Haskell?

An instance of a class is an individual object which belongs to that class. In Haskell, the class system is (roughly speaking) a way to group similar types. (This is the reason we call them "type classes"). An instance of a class is an individual type which belongs to that class.

How do I check my type in Haskell?

If you are using an interactive Haskell prompt (like GHCi) you can type :t <expression> and that will give you the type of an expression. e.g. or e.g.


1 Answers

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.

like image 109
chepner Avatar answered Nov 15 '22 06:11

chepner