1
votes

I'm now learning Haskell and trying to make my data (which is similar to Maybe) an instance of Eq, as follows.

module Main where

data MyMaybe a = MyNothing | MyJust a

instance (Eq a) => Eq (MyMaybe a) where
  MyNothing == MyNothing = True
  MyJust x == MyJust y = x == y
  _ == _ = False

main :: IO ()
main = do
  let b0 = MyJust 42 == MyJust 42 -- OK
  print b0                        -- True
  let b1 = MyNothing == MyNothing -- Build error!
  print b1

But the complier returns an error in the line let b1 = MyNothing == MyNothing as follows.

    ? Ambiguous type variable ‘a0’ arising from a use of ‘==’       prevents the constraint ‘(Eq a0)’ from being solved.       Probable fix: use a type annotation to specify what ‘a0’ should be.       These potential instances exist:         instance Eq Ordering -- Defined in ‘GHC.Classes’

...

How can I fix it?

... And I have searched existing questions and answers. The following one seemed close to mine, but I think that my code has already solved the points that the answer suggested.

how to instance Eq without deriving

2
It has mothing to do with MyMaybe specifically, you will get the same result with the standard Maybe. Demo.n. 1.8e9-where's-my-share m.

2 Answers

2
votes

The problem is that MyMaybe is a parametrised type, but in the expression MyNothing == MyNothing, the compiler can't figure out if you meant MyMaybe Int, MyMaybe String, MyMaybe (Int -> Bool), or something else.

You can help it with a type annotation like this:

let b1 = MyNothing == (MyNothing :: MyMaybe Int)
2
votes

The problem is that MyNothing has type MyMaybe a with no restrictions on what a can be.

When you write MyNothing == MyNothing, then the type of (==) :: (Eq b) => b -> b -> Bool restricts the two MyNothing to have the same type (the compiler initially starts out with MyNothing :: MyMaybe a0 and MyNothing :: MyMaybe a1, but then realizes that a0 == a1).

But to resolve == itself, the compiler needs to find the right Eq instance (because that's where == is defined). Our type is MyMaybe a, which is good: We have an Eq (MyMaybe a) instance right there. But it also requires an Eq a instance, which cannot be resolved because we don't know what a is. (It doesn't matter that (Eq a) isn't actually used in the MyNothing == MyNothing case; type checking doesn't pay attention to runtime values.)

This is the point where the compiler gives up, saying it can't resolve the constraint (Eq a0) because the type variable a0 is ambiguous.


Fixing this is easy. Just give the compiler a hint which type should be used:

let b1 = MyNothing == (MyNothing :: MyMaybe Integer)   -- for example

Or:

let b1 = MyNothing == (MyNothing :: MyMaybe (Double, [String]))

The actual type doesn't really matter (as long as it has an Eq instance).


This ambiguity isn't limited to your MyMaybe type. You should get the same error with Maybe (Nothing == Nothing) or [] ([] == []). In all cases there's a polymorphic constant involved (Nothing :: Maybe a, [] :: [a]).


Your example does actually work in ghci. This is because ghci uses extended defaulting rules and ends up converting a to (); i.e. the comparison is done as MyNothing == (MyNothing :: MyMaybe ()).