Reputation: 2967
I'm going over some basic typeclass implementations:
data Colour = Red | Green | Black
class BasicEquality a where
isEqual :: a -> a -> Bool
instance BasicEquality Bool where
isEqual True True = True
isEqual False True = True
isEqual True False = True
isEqual _ _ = False
instance BasicEquality Colour where
isEqual Red Green = True
isEqual _ _ = False
instance BasicEquality Int where
isEqual 100 100 = True
isEqual _ _ = False
main = do
print $ isEqual Red Green //Output: True
print $ isEqual 100 100 //Output: Error Ambiguous type variable ‘a0’ arising from a use of ‘isEqual’
Clearly, this works when I specify print $ isEqual (100 :: Int) (100 :: Int)
Why does Haskell implicitly recognise that Red
and Green
are Colours
yet requires me to specifically bind 100
to Int
?
Upvotes: 0
Views: 37
Reputation: 120711
Red
etc. are monomorphic values, i.e. they have a concrete type
Red :: Colour
So when Red
turns up in your code, the compiler knows immediately what type it is, and it can use this information to infer what typeclass instance to use for e.g. BasicEquality
.
OTOH, numeric literals like 100
have the polymorphic type Num a => a
. The reason is that we want to be able to write
Prelude> replicate 3 'q'
"qqq"
as well as
Prelude> sqrt 3
1.7320508075688772
If 3
had simply the type Int
, the latter would not work, because sqrt
expects a type of e.g. Double
. Since numerical literals are actually polymorphic, this doesn't matter.
The trouble in your case is that isEqual
is also polymorphic. There are thus multiple different types the compiler could select. Image you also had
instance BasicEquality Integer where
isEqual 50 1000 = True
isEqual _ _ = False
then isEqual 100 100
could be interpreted as either isEqual (100 :: Int) 100
(which is True
) or isEqual (100 :: Integer) 100
(which is false).
In practice, this is seldom so much of an issue, because you won't be comparing different numerical literals (these are already known, so you could as well simply hard-code the result!) but at most one literal with one variable in your program, and that variable will usually have already a type determined from the context. For instance,
*Main> let b = length "foobar"
*Main> isEqual 4 b
False
works without any signature.
Upvotes: 3