Search code examples
haskellghcghciwinghci

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


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?


Solution

  • 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