Haskell : understanding "No instance for" error messages in ghci

artella picture artella · Apr 29, 2012 · Viewed 13.1k times · Source

Question 1

Hi, if in WinGHCi I intentionally do the following wrong piece of code :

3 4

Then the error message I get is

<interactive>:1:1:
    No instance for (Num (a0 -> t0))
      arising from the literal `3'
    Possible fix: add an instance declaration for (Num (a0 -> t0))
    In the expression: 3
    In the expression: 3 4
    In an equation for `it': it = 3 4

What exactly does No instance for (Num (a0 -> t0)) mean?

Question 2

Why does the following piece of code :

(+) 2 3 4
<interactive>:1:7:
    No instance for (Num (a0 -> t0))
      arising from the literal `3'
    Possible fix: add an instance declaration for (Num (a0 -> t0))
    In the second argument of `(+)', namely `3'
    In the expression: (+) 2 3 4
    In an equation for `it': it = (+) 2 3 4

yield a slightly different error from the second piece of code :

2+3 4
<interactive>:1:3:
    No instance for (Num (a1 -> a0))
      arising from the literal `3'
    Possible fix: add an instance declaration for (Num (a1 -> a0))
    In the expression: 3
    In the second argument of `(+)', namely `3 4'
    In the expression: 2 + 3 4

Namely in the first piece of code we have No instance for (Num (a0 -> t0)) where as in the second piece of code we have No instance for (Num (a1 -> a0)).


[Response to ehird]

(Questions moved from answer comments) :

1) I appreciate the latter two expressions are different, but are you saying that I should not try to understand why the interpreter chooses (Num (a0 -> t0)) for the former and (Num(a1 -> a0)) for the latter, besides the fact that they are different?

2)Hi, and with the former when you say "But there's no Num instance for functions" what do you mean? Sorry I am not clear on what the concept of an instance is. Furthermore, just out of curiosity, could you use your instance Num (a -> b) method to somehow tell the interpreter to interpret 3 4 as 4 modulo 3?

Answer

Chris Taylor picture Chris Taylor · Apr 30, 2012

My intention is to complement ehird's answer with a little bit more explanation. When you wrote the expression

3 4

Then the Haskell interpreter thinks that you are trying to apply the function 3 to whatever 4 is. In order for Haskell to interpret 3 as a function, it needs to make a call to the function

fromInteger :: Integer -> (a -> b)

in order to get a function (i.e. something of type a -> b) from the integer 3. Now, fromInteger is defined in the Num typeclass to have the signature

instance Num x where
    fromInteger :: Integer -> x

i.e. when you make the type x an instance of the Num class, you give an implementation of fromInteger which tells Haskell how to convert an integer literal into an x. In your case, x is the function type a -> b. So let's do it!


First, some boilerplate. To make x an instance of Num Haskell requires that we also make it an instance of Show and Eq:

instance Show (a -> b) where show f = "<function>"
instance Eq (a -> b) where f == g = False

Now let's say we want to interpret 3 4 as "4 modulo 3". Then we need to tell Haskell how to interpret any integer as a function that calls mod. Moreover, since mod only accepts integral types (it has the signature mod :: Integral a => a -> a -> a) then we need to restrict the types of a and b to be integral as well:

instance (Integral a, Integral b) => Num (a -> b) where

To make an instance of Num we need to give implementations of (+), (-), (*) and fromIntegral (actually we should define a couple of other functions too, but let's not worry about that now).

There's a fairly natural way to define addition, subtraction and multiplication (all code from here forms part of the Num instance and should be indented relative to the instance declaration)

    f + g = \x -> f x + g x
    f - g = \x -> f x - g x
    f * g = \x -> f x * g x

i.e. when you add two functions f and g, you get a new function that applies both f and g to its argument, and then adds them together. Since we required that the result of applying f and g was of integral type, we know that it makes sense to add up their outputs.

To interpret an integer as a function we can write

    fromInteger n = \m -> fromIntegral m `mod` fromIntegral n

i.e. when we have an integer n, we return a function of a parameter m that, when called, ensures that both arguments are of the same type (by calling fromIntegral on both of them) and then uses them as arguments to the function mod.

Finally, a bit more boilerplate to stop Haskell complaining:

    abs f = undefined
    signum f = undefined

We can test this out. I have my code in a file called numfun.hs. I boot up the Haskell interpreter and load my file:

Prelude> :l numfun.hs
[1 of 1] Compiling Main             ( numfun.hs, interpreted )
Ok, modules loaded: Main.

Now I can define some functions:

*Main> let f = (+ 1)
*Main> let g = (* 2)

I can add them or subtract them:

*Main> (f + g) 3   -- (3+1) + (3*2)
10
*Main> (f - g) 3   -- (3+1) - (3*2)
-2

And I can call numbers as functions:

*Main> 3 4         -- 4 `mod` 3
1