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
?
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