Reputation: 59
I start to learn Haskell. While studying a tutorial I have found the following example that enables the usage of functions in arithmetic expressions:
module FunNat where
instance Num a => Num (t -> a) where
(+) = fun2 (+)
(*) = fun2 (*)
(-) = fun2 (-)
abs = fun1 abs
signum = fun1 signum
fromInteger = const . fromInteger
fun1 :: (a -> b) -> ((t -> a) -> (t -> b))
fun1 = (.)
fun2 :: (a -> b -> c) -> ((t -> a) -> (t -> b) -> (t -> c))
fun2 op a b = \t -> a t `op` b t
The example works. But I can not understand how the (+)
function is transformed into the function of two arguments. As I can understand each (+)
is substituted with fun2 (+)
. And fun2 is equivalent to the function of one argument \t -> a t 'op' b t
, but we should have a function of two arguments (something like (\t1 t2 -> (\x -> x ) t1 + (\x -> x) t2)
). I think that here some basic concepts of Haskell typing should be applied but I do not know what they are.
EDIT 1
I understand that fun2
is a function of three arguments. I can't understand internal expression transformation. I'm reasoning in the following way: (+) 3 4
= (+) (\x->3) (\x->4)
= fun2 (+) (\x->3) (\x->4)
= \t -> (\x->3) t + (\x->4) t
What is this t? Or where am I wrong in my reasoning? May be it is necessary to think another way?
EDIT 2
I think that I have reached some understanding of the issue (thanks you all!). So:
When we write (+) 3 4
- in this case a simple operation from Num
is used, no special features from FunNat
. To use (+)
from FunNat
it is necessary to write fun2 (+) 3 4
or fun2 (+) (\x -> 3) (\x -> 4)
. But these expressions expect any third parameter to be evaluated;
To demonstrate specific features from FunNat
we may use the following example ((*)-(+))
(taken from tutorial) or in other form - (-) (*) (+)
. Both expressions take two arguments. In this case we have: ((*)-(+))
= fun2 (-) (*) (+)
= \t -> (*) t - (+) t
= \t -> (\t1 t2 -> t1 * t2) t - (\t1 t2 -> t1 + t2) t
= (\t t2 -> t * t2) - (\t t2 -> t + t2)
= \t t2 -> (t * t2) - (t + t2)
. The final expression expects just Num
. I hope that all these are correct
As it was explained in tutorial, but I can't understand what for, the possibility to use (*)
and (+)
as parameters for fun2
that expects (t->a)
is based on the following (taken from tutorial): t1 -> t2 -> a
= t1->a'
where a' = t2 -> a
. Here curring is used.
So all necessary facts were on the surface but I do not take into consideration the non-trivial mechanism of type inference.
Upvotes: 0
Views: 136
Reputation: 6778
The type of (+)
is Num a => a -> a -> a
. Since you're creating an instance of Num
for the type Num a => t -> a
, consider the explicit signature
(+) :: Num a => (t -> a) -> (t -> a) -> (t -> a)
(1)
or equivalently (by currying)
(+) :: Num a => (t -> a) -> (t -> a) -> t -> a
(2)
What this says is: "If you give me 2 strategies for producing a Num a => a
from a t
, I can create a new strategy for producing a Num a => a
from a t
by using the Num.(+)
instance of the a
's"
I think the part that has you confused is that this specific (+)
can be viewed as a function of 2 arguments which returns a function (1), or a function of 3 arguments which returns a value (2). Since functions in Haskell are curried, these are really the same thing.
Upvotes: 1
Reputation: 52039
In type signatures parentheses associate to the right, so:
f :: a -> (b -> c -> d)
is the same as:
f :: a -> b -> c -> d
So the parens indicated below are redundant:
fun2 :: (a -> b -> c) -> ((t -> a) -> (t -> b) -> (t -> c))
^ ^
and removing them leaves:
fun2 :: (a -> b -> c) -> (t -> a) -> (t -> b) -> (t -> c)
arg1 arg2 arg3
Upvotes: 0