Why does ghci list an equality type constraint in the type signature for this function matchInt
which I constructed via pattern matching:
$ ghci
GHCi, version 8.2.1: http://www.haskell.org/ghc/ :? for help
Prelude> :{
Prelude| matchInt 1 = 3
Prelude| matchInt _ = 4
Prelude| :}
Prelude> matchInt 1
3
Prelude> matchInt 22
4
Prelude> :t matchInt
matchInt :: (Eq a, Num a, Num p) => a -> p
In contrast, when using a simple data constructor, there is no equality type constraint.
$ ghci
GHCi, version 8.2.1: http://www.haskell.org/ghc/ :? for help
Prelude> data Z = Y Int
Prelude> :{
Prelude| matchDataInt (Y 1) = 3
Prelude| matchDataInt _ = 4
Prelude| :}
Prelude> matchDataInt (Y 1)
3
Prelude> matchDataInt (Y 22)
4
Prelude> :t matchDataInt
matchDataInt :: Num p => Z -> p
Indeed instances of Z can not be compared:
Prelude> Y 22 == Y 33
<interactive>:11:1: error:
• No instance for (Eq Z) arising from a use of ‘==’
• In the expression: Y 22 == Y 33
In an equation for ‘it’: it = Y 22 == Y 33
So again, why does the matchInt
function list equality as a type constraint but not the function matchDataInt
?
This question is related. However, if an equality test is needed for matchInt
then why isn't it needed for matchDataInt
? And here I come to my key point: don't both matchInt
and matchDataInt
have to test against 1 for the pattern matching to operate?
Syntactically matchInt
is built on a pattern match, but the patern match here is an illusion. 1
is not a data constructor. Numeric literals are overloaded. 1
is equivalent to fromInteger #1
where #1
is a non-overloaded litteral (not expressible in standard Haskell) of type Integer
. You cannot really pattern match against such things.
So the compiler lets you write what is syntactically a pattern match, but this notation really denotes a guard:
matchInt 1 = ... -- what is written
matchInt x | x == fromInteger #1 = ... -- what it really means
Since the type of matchInt
is not explicitly given, it's inferred. It's a function, so the type is some refinement of a->b
. The call to fromInteger
gives rise to the constraint Num a
, and the call to ==
gives rise to the constraint Eq a
, and that's all we can tell about a
.
If OTOH we give the function an explicit signature, say
matchInt :: Int->Int
then we don't need to infer the type, but only check if it satisfies the constraints. Since Int
satisfies both Eq Int
and Num Int
, everything is ok.
And this is what's going on in your second example. The type you match is known to be Int
, not because of an explicit type signature but because it is inferred from the Y Int
alternative of Z
. Here again Int
already has all the needed instances.
The matchDataInt
function doesn't require an Eq
constraint because it specifically matches on an Int
, and Int
s already have an Eq
instance.
Your matchInt
function doesn't just take Int
s as parameters – it can take any kind of number, as long as you can compare that number for equality. That's why it has type (Eq a, Num a, Num p) => a -> p
. You could also give it the type Num p => Int -> p
(specializing a
to Int
), because Int
already has Eq
and Num
instances.
Your matchDataInt
function, on the other hand, takes a Z
as an argument, and every Z
contains an Int
. Pattern matching against that Int
incurs an Eq
constraint, but only on the Int
. You could instead have
data Z' a = Y' a
matchDataNum :: (Eq a, Num a, Num p) => Z' a -> p
matchDataNum (Y' 1) = 3
matchDataNum _ = 4
And here, you couldn't remove the Eq a
constraint.
This all might be slightly clearer with variant functions that didn't return numbers themselves. If we have
data Z = Y Int
data Z' a = Y' a
is1 1 = True
is1 _ = False
isY1 (Y 1) = True
isY1 _ = False
isY'1 (Y' 1) = True
isY'1 _ = False
then the three functions we've defined have the types
is1 :: (Eq a, Num a) => a -> Bool
isY1 :: Z -> Bool
isY'1 :: (Eq a, Num a) => Z' a -> Bool
We could also define
is1Int :: Int -> Bool
is1Int 1 = True
is1Int _ = False
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