Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell composition with two parameters

I'm trying to understand functional programming through Haskell and I'm having so much trouble dealing with function composition.

Actually I have these two functions :

add:: Integer -> Integer -> Integer
add x y =  x + y

sub:: Integer -> Integer -> Integer
sub x y =  x - y

I want to be able to compose them. It doesn't make any sense okay, but it's for a learning aim.

What I've tried :

foo:: (Integer -> Integer) -> (Integer -> Integer) -> Integer
foo = add . sub

What I've understood :

Haskell uses functions with only one args, so that we return a new function to execute after each function execution.

So the first Integer is the param type, while the second is the return type of a generated function that has to add the second number.

This will return another function (sub) that will makes the same flow (returning a function with params etc...)

Am I right ?

Here's my actual error code :

src\Main.hs:23:7:
    Couldn't match type `Integer' with `Integer -> Integer'
    Expected type: Integer -> (Integer -> Integer) -> Integer
      Actual type: Integer -> Integer -> Integer
    In the first argument of `(.)', namely `add'
    In the expression: add . sub

src\Main.hs:23:13:
    Couldn't match type `Integer -> Integer' with `Integer'
    Expected type: (Integer -> Integer) -> Integer
      Actual type: Integer -> Integer -> Integer
    Probable cause: `sub' is applied to too few arguments
    In the second argument of `(.)', namely `sub'
    In the expression: add . sub

I don't know what I'm doing wrong.

Can you help me understanding a bit more this error so I can find a solution ?

like image 932
Thomas thomas Avatar asked Jan 12 '16 07:01

Thomas thomas


2 Answers

Given a function

add :: Integer -> Integer -> Integer

it's useful to remember (as you pointed out yourself in the What I understood section) that -> in type signatures associates to the right, i.e. the above type is the same as

add :: Integer -> (Integer -> Integer)

Now, consider the type of (.):

(.) :: (b -> c) -> (a -> b) -> a -> c

This means that in an expression

(.) add

The b in the type of (.) is Integer and the c corresponds to Integer -> Integer. Another way to write this is

b ~ Integer
c ~ Integer -> Integer

So we get

(.) add :: (a -> Integer) -> a -> (Integer -> Integer)

If you now apply (.) add to sub, the compiler notices that a -> Integer cannot be made to match Integer -> Integer -> Integer.

I suspect that you probably want the composition to take three arguments: two to apply sub to, and the result of that is then - together with the third argument - passed to add. So a possible definition which composes the two functions would be

foo :: (Integer -> Integer -> Integer) -> (Integer -> Integer -> Integer) -> Integer -> Integer -> Integer
foo f g x y = f (g x y) y

For what it's worth, there's a related problem: composing a two argument function with a one argument function, e.g. composing

like image 109
Frerich Raabe Avatar answered Oct 03 '22 08:10

Frerich Raabe


I want to be able to compose them. It doesn't make any sense okay, but it's for a learning aim.

That's actually the problem here. How do you want to compose them? Let us have a look at some possible compositions:

foo x y = sub x (add x y)          -- x + y - x = y
foo x y = sub y (add x y)          -- x + y - y = x
foo x y = sub x (add y y)          -- 2 * y - x 
foo x y = sub y (add y y)          -- 2 * y - y = y
foo x y = sub y (sub y (add x x))  -- 2 * x - 2 * y

That being said, let us inspect the type error by checking the types from hand:

type I = Integer -- otherwise the lines are going to be very long

(.)         :: (b ->    c   ) -> (a ->   b   ) -> a -> c
add         ::  I -> (I -> I)
sub         ::                   I -> (I -> I)
--                               |||||||||||||
(.) add     ::                   (a  -> I    ) -> a -> (I -> I)
--                               ^^^^^^^^^^^^^

As you can see, (.) add already mandates that the other function may only have type a -> Integer for an arbitrary a. But sub's type is Integer -> (Integer -> Integer) (remember, (->) is right associative).

Now, what can you do to actually fix this? First, let us inspect your proposed type of foo:

foo :: (Integer -> Integer) -> (Integer -> Integer) -> Integer

That's actually a very interesting type of function. How would you actually get your result? You're just have two functions at hand, but no values:

> foo f g = 

You can solve this by using a fixed point of one of the functions and then apply the other:

>   let x = f x in g x
>
> example = foo (const 12) (+1) -- returns 13

But that's not what you meant, right? At this point, it's very important to think about the semantics of your composition. Since those are not clear, you cannot write a general way to compose both functions here.

However, if you actually meant

foo :: Integer -> Integer -> Integer -> Integer
foo x y z = add (sub x y) z

then that's possible with

foo = (add .) . sub

since

(.) add        :: (a -> I) -> a -> (I -> I)
(.) ((.) add)  :: (a -> b -> Integer) -> a -> b -> Integer -> Integer

But (add .) . sub isn't really easy to the eye anymore. You're better off writing a point-wise definition of foo if this kind of function was your original goal.

like image 37
Zeta Avatar answered Oct 03 '22 07:10

Zeta