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:
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.