Does Haskell support the composition of partially applied functions?

For example, I can’t do

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

which is good because i can always do

 f = (*2).(+3) 

but it is a little inconvenient when I want to create a list of functions, for example

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

whereas

 map (+) [1, 2, 3] 

will be fine.

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

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

As far as I can tell, GHC does not like to make partially applied functions, because it does not know how to submit function types to operations of type (* 2).

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

But I always thought that this should be quite natural: the output type (+) is Num a => a, therefore (* 2) should be able to "eat" it.

My question is: is this somehow implemented? Or does anyone know why such a simple thing is not implemented in Haskell?

+5
source share
2 answers

Haskell supports the composition of partial functions, but you have a type mismatch. Composition is a feature

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

In your case, you have two functions

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

and when you create these functions, the result type will be

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

this is not what you want. You can rewrite your function f , for example (.) (*2) . (+) (.) (*2) . (+) , but I think lambda is more readable.

And you confuse the partial function and the partial application. A partial function is a function that is not defined for the entire domain, and a partial application is the process of fixing a number of function arguments that create a function of lesser arity.

+6
source

When you go to a point, this can be confusing. If we go through type signatures,

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

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

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

Now if we do (2*) . (+) (2*) . (+) second parameter (.) , which is (+) , should be of type (a -> b) , which is really true when we rewrite (+) type as Num a => a -> (a -> a) Therefore, our b type is Num a => a -> a . Now remember that (.) Takes (b -> c) as the first parameter. We have a little problem here, because in our expression (2*) . (+) (2*) . (+) (2*) waiting for type b type Num a => a , while we have our type b , for example Num a => a -> a . You cannot multiply a function by 2 ..! We need to convert (2*) to a function that takes a function of our type, which Num a => a -> a , starts it and feeds the result to (2*) . There is no need to watch it no more (.) . In this case, if we write again as ((2*) . ) , We can safely pass our function like Num a => a -> a (remember that this is partially applied (+) ). Let's finish it ...

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

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

We can use applicative functors like ((->) r) to make it easier to express ourselves in these situations. I will try to add it as soon as I have more time.

+1
source

Source: https://habr.com/ru/post/1271445/


All Articles