Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does Haskell support composition of partially applied functions?

Tags:

haskell

For example, I can't do

f = ((*2).(+)) 3

which is alright since I can always do

f = (*2).(+3)

but this is slightly inconvenient when I want to create a list of functions, e.g.

map ((*2).(+)) [1, 2, 3]

whereas

map (+) [1, 2, 3]

would be alright.

Of course, I can always use lambda notation to implement currying:

map (\x y -> 2*(x + y)) [1, 2, 3]

As far as I can tell, GHC doesn't like composing partially applied functions because it doesn't know how to feed function types to operations like (*2).

(+) 2 :: Num a => a -> a 
(*2) :: Num a => a -> a 

But I've always thought it should be a rather natural thing: the type of the output of (+) is Num a => a, so (*2) should be able to 'eat' that.

My question is: is this implemented in some way? Or, does someone have any idea why such a straightforward thing isn't implemented in Haskell?

like image 564
Raskell Avatar asked Sep 02 '17 07:09

Raskell


People also ask

What is partial application Haskell?

Partial application in Haskell involves passing less than the full number of arguments to a function that takes multiple arguments.

What is function composition in Haskell?

Composing functions is a common and useful way to create new functions in Haskell. Haskell composition is based on the idea of function composition in mathematics. In mathematics, if you have two functions f(x) and g(x), you compute their composition as f(g(x)). The expression f(g(x)) first calls g and then calls f.

What is partial application functional programming?

In computer science, partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity. Given a function , we might fix (or 'bind') the first argument, producing a function of type .

What is the difference between currying and partial application?

Currying tends to produce nested unary(1-ary) functions. However, the transformed functions are still broadly similar to the original one. Partial application tends to produce functions that have an arbitrary number of arguments, and the transformed functions usually have a lot of differences from the original one.


2 Answers

Haskell support composition of partial functions but you have type mismatch. Composition is function

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

In your case you have two function

(*2) :: Num a => a -> a
(+)  :: Num a => a -> a -> a

and when you compose this functions type of result will be

((*2).(+)) :: (Num (a -> a), Num a) => a -> a -> a

that not what you want. You can rewrite your function f such as (.) (*2) . (+) but I think that lambda is more readable.

And you confuse partial function and partial application. Partial function is function that defined not for the entire domain and partial application is the process of fixing a number of arguments to a function, producing function of smaller arity.

like image 109
Igor Fedorov Avatar answered Nov 15 '22 07:11

Igor Fedorov


When going to point-free it might get confusing. If we go through the type signatures;

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

(+) is Num a => a -> a -> a

(2*) is Num a => a -> a

Now if we do (2*) . (+) being the second parameter of (.) which is (+), has to have a type (a -> b) which is in fact true when we rewrite (+)s type as Num a => a -> (a -> a). So our b type happens to be Num a => a -> a. Now remember that (.) takes (b -> c) as first parameter. We have a little problem here because in our expression (2*) . (+) (2*) waits for a b type like Num a => a while we have our b type like Num a => a -> a. You can't multiply a function by 2..! We must transform (2*) into such a function that would take a function of our type which is Num a => a -> a, run it and feed it's result to (2*). No need to look this is no more than (.). In this case if we write again like ((2*) . ) we can safely feed our Num a => a -> a type function (remember this is partially applied (+)). Let us wrap it up...

Our point free equivalent of \x y -> 2*(x + y) is ((2*) . ) . (+)

Prelude> (((2*) . ) . (+)) 3 5
16

We may use applicative functors of ((->) r) type to express ourselves more easily in these situations. I will try to add it once i have more time.

like image 34
Redu Avatar answered Nov 15 '22 08:11

Redu