Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous type variable `a0' in the constraints

Tags:

haskell

I'm trying to go through the YesNo example from Learn You a Haskell for Great Good! book.

Here is my source code:

module Main where

main :: IO ()

main = putStrLn ( show (yesno 12) )

class YesNo a where
    yesno :: a -> Bool


instance YesNo Bool where
    yesno b = b

instance YesNo [a] where
    yesno [] = False
    yesno _ = True


instance YesNo Int where
    yesno 0 = False
    yesno _ = True

When I execute this code following exception occurs:

Ambiguous type variable `a0' in the constraints:
  (YesNo a0) arising from a use of `yesno'
             at /Users/mkhadikov/Projects/personal/haskell/hello-world/yesno.hs:5:25-29
  (Num a0) arising from the literal `12'
           at /Users/mkhadikov/Projects/personal/haskell/hello-world/yesno.hs:5:31-32
Probable fix: add a type signature that fixes these type variable(s)
In the first argument of `show', namely `(yesno 12)'
In the first argument of `putStrLn', namely `(show (yesno 12))'
In the expression: putStrLn (show (yesno 12))

Can you please explain what's wrong with this code?

like image 664
Mairbek Khadikov Avatar asked Dec 17 '11 16:12

Mairbek Khadikov


3 Answers

The problem is that it doesn't know what type 12 is! It can be any type with a Num instance:

GHCi> :t 12
12 :: Num a => a

You need to specify the type you want directly: try putStrLn (show (yesno (12 :: Int))).

Why can't GHC pick Int, since no other choice would work, you ask? Good question. The answer is that with Haskell's typeclass system, adding an instance can never invalidate existing correct programs or change their behaviour. (This is referred to as the open world assumption.) If it did pick Int, then what would happen if you added instance YesNo Integer? The choice would become ambiguous, and your program would break!

So when you want to use a typeclass like this with a polymorphic value, you have to specify what type you mean more precisely. This shouldn't come up much in practice, since there'll usually be some surrounding context to force the type to be what you want; it's mainly numeric literals that are affected by this.

like image 125
ehird Avatar answered Nov 15 '22 08:11

ehird


The problem is that 12 actually has type Num a => a and not Int as you expect. If you add an explicit type anotation such as 12 :: Int, it should compile.

like image 41
fuz Avatar answered Nov 15 '22 09:11

fuz


I had the same problem.

This is a solution, perhaps not the best one but it works:

class YesNo a where
     yesno  ::  a -> Bool


instance YesNo Int where
     yesno 0    =   False
     yesno _    =   True


instance YesNo Integer where
     yesno 0    =   False
     yesno _    =   True
like image 37
jimmyt Avatar answered Nov 15 '22 10:11

jimmyt