Why does a function built using pattern matching have an Eq type restriction, but not when using a data constructor?

Why does ghci list the equality type constraint in the type signature for this matchInt function, which I built using 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 may 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, why does matchInt represent a list as a type constraint, but not a matchDataInt function?

This question is related. However, if an equality test is needed for matchDataInt , then why is it not needed for matchDataInt ? And here I come to my key point: should not both matchInt and matchDataInt be tested against 1 in order to match the pattern to work?

+7
equality algebraic-data-types haskell typeclass ghci
source share
2 answers

Syntactically matchInt built to match the pattern, but the patern match here is an illusion. 1 not a data constructor. Numeric literals are overloaded. 1 equivalent to fromInteger #1 , where #1 is an unloaded script (not expressed in standard Haskell) of type Integer . You cannot compare with such things.

So, the compiler allows you to write syntactically match the pattern, but this notation really means protection:

 matchInt 1 = ... -- what is written matchInt x | x == fromInteger #1 = ... -- what it really means 

Since matchInt is not explicitly specified, it is inferred. This is a function, so the type is some refinement a->b . Calling fromInteger calls the Num a constraint, and calling == calls the Eq a constraint, and all we can say about a .

If OTOH we give the function an explicit signature, let's say

 matchInt :: Int->Int 

then we do not need to deduce the type, but check only if it satisfies the constraints. Since Int satisfies both Eq Int and Num Int , everything is fine.

And this is what happens in the second example. The type you map is known as Int , not because of the explicit type signature, but because it is derived from an alternative to Y Int Z Here again, Int already has all the necessary instances.

+10
source share

The matchDataInt function matchDataInt not require an Eq constraint, since it specifically matches on Int , and Int already has an Eq instance.

Your matchInt function matchInt not just accept Int as parameters - it can accept any number if you can compare this number for equality. That is why it is of type (Eq a, Num a, Num p) => a -> p . You can also specify the type Num p => Int -> p (specializing a in Int ), since Int already has instances of Eq and Num .

On the other hand, your matchDataInt function takes Z as an argument, and each Z contains an Int . Matching the template to this Int causes an Eq constraint, but only to Int . Instead you can

 data Z' a = Y' a matchDataNum :: (Eq a, Num a, Num p) => Z' a -> p matchDataNum (Y' 1) = 3 matchDataNum _ = 4 

And here you cannot remove the restriction Eq a .


All of this can be a little clearer with variant functions that themselves did not return numbers. 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 that we defined are of 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 
+7
source share

All Articles