DrDecay
DrDecay

Reputation: 59

Haskell. Functions in Num expressions

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:

  1. 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;

  2. 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

  3. 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

Answers (2)

cdk
cdk

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

ErikR
ErikR

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

Related Questions