5

Why can't Haskell perform pattern matching on Num types, without us specifying Eq as a type class?

For instance:

h :: Num a => a -> a
h 0 = -1
h x = x + 1

When compiling this function, ghci complains:

    * Could not deduce (Eq a) arising from the literal `0'
      from the context: Num a
        bound by the type signature for:
                   h :: forall a. Num a => a -> a
        at functions.hs:9:1-20
      Possible fix:
        add (Eq a) to the context of
          the type signature for:
            h :: forall a. Num a => a -> a
    * In the pattern: 0
      In an equation for `h': h 0 = - 1
   |
10 | h 0 = -1
   |   ^

Changing the function definition as following compiles and runs perfectly:

h :: (Num a, Eq a) => a -> a
h 0 = -1
h x = x + 1

*Main> h 0
-1
*Main>
Guillaume
  • 1,814
  • 1
  • 13
  • 29
  • 1
    It needs to be able to determine when the value is `== 0` to check against your pattern, since `0` is a `Num a => a` and not a data constructor. – Ry- Feb 11 '18 at 05:53

1 Answers1

6

From the Haskell 2010 Report, the section entitled Informal Semantics of Pattern Matching:

Matching a numeric, character, or string literal pattern k against a value v succeeds if v == k

So when you use a literal (such as 0) as a pattern, its meaning depends upon == (a method of the Eq class).

For example, your function h

h 0 = -1
h x = x + 1

can be rewritten as

h x | x == 0 = -1
h x          = x + 1

You are (implicitly) using the == method, therefore you need an Eq constraint.

There are two important observations here about how Haskell differs from a lot of other languages:

  1. The notion of equality is not defined for all types. One cannot ask whether x == y unless the type of x and y has an Eq instance.
  2. The set of numeric types is not fixed. A numeric literal can take on any type that has an instance of Num. You can define your own type and make it an instance of Num, and it doesn't necessarily have to also have an instance of Eq. So not all "numbers" can be compared for equality.

So it is insufficient for the context of your function h to be "a has to be a number." The context must be, more specifically, "a has to be a number with an equality test" to ensure that there is a way to check whether x is equal to 0 in order to perform the pattern match.

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
  • Now I’m curious: is there any `Num` instance *without* equality? Or are they just keeping that typeclass optional, just in case? Is that so you could, choose between different equivalence relations: `1/2 == 2/4`, or not, `Expr 1 + Expr 1 == Expr 2` or not? – Davislor Feb 11 '18 at 06:38
  • 2
    @Davislor The example I've seen quoted all over the place is "you can make a sensiblish instance for functions" (e.g. https://wiki.haskell.org/Num_instance_for_functions), and functions *can't* have a good `Eq` instance. But that usually comes with the caveat "you probably shouldn't use that `Num` instance". Another possibility that springs to mind is a data type representing expressions with variables. The operations in `Num` really don't have anything to do with the operations in `Eq`, so saying when you couldn't have one without providing the other it was less useful than it is now. – Ben Feb 11 '18 at 06:53
  • But *surely* Haskell can represent equality over the Church numerals? All those professors of computer science can’t have overlooked that. – Davislor Feb 11 '18 at 08:10
  • And more seriously, since GHC can pass function pointers around, I’m pretty sure there has to be a way to coerce it to implement some form of identity. But this is veering off onto a tangent. Thanks for the great examples! – Davislor Feb 11 '18 at 08:14
  • 2
    @Davislor GHC does let you peer into the runtime internals; check out the `unsafeAddr` function defined in [this answer](https://stackoverflow.com/a/18563789/402884). But even if you do compare functions by the memory addresses at which they are represented, it still isn't a *good* equality test, because it will produce `False` in a lot of cases where you're comparing two different representations of what is really the same function. If you want "reference equality," the proper way is to use `IORef` (which does have an `Eq` instance) or something similar. – Chris Martin Feb 11 '18 at 17:29