6
votes

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?

2
Your terminology here seems a little confused—“partial functions” are a quite distinct concept from what you are talking about in this question—but that’s mostly a separate problem. The core issue here is that (f . g) x is equivalent to (f (g x)) but you are hoping that ((* 2) . (+)) n will be the same as ((* 2) . (+ n)), when clearly it is really (* 2) ((+) n) by the definition of (.), which is not the same. You need a different function than (.) for that sort of composition. - Alexis King
Your first example f = ((*2).(+)) 3 can be written as f = (.) (*2) . (+) $ 3. The pattern to do this is: (.) f1 . f2, where f1 and f2 are functions that take 1 and 2 arguments respectively. - mschmidt
... and using that, you can do map ( (.) (*2) . (+) ) [1,2,3] which gives you a list of functions of type Num a => a -> a each. - Thilo
@mschmidt You could expand that into an answer. - chi
@AlexisKing Yes, I did indeed mean partially applied functions. Let me amend the question. - Raskell

2 Answers

7
votes

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.

4
votes

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.