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?
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With