Reputation: 55
I'm trying to write a pipeline function that rolls an value through a list of functions from the right. The function looks like this:
map . foldr (.) id
But I don't understand what is it doing here? I do know that .
is function composition, but what does (.)
do?
Upvotes: 1
Views: 166
Reputation: 28366
The difference between .
and (.)
is the same as that between +
and (+)
, -
and (-)
, and all the others:
f . g
or 3 + 5
;(.) f g
or (+) 3 5
.Something similar is true also for functions with "normal" names, such as myfunc
. In this case, however, the plain name myfunc
wants the arguments after it (just like (.)
), and the infix form is obtained by putting the name between backticks.
Here are a few equalities as examples:
f . g === (.) f g
a + b === (+) a b
a `fun` b === fun a b
i `elem` l === elem i l
As an additional info, even if a function takes more than 2 arguments, as in
func x y z = whatever
you can still call it with infix style, but writing a `func` b
will give you a function taking one argument (the one corresponding to z
), just like you had done func a b
. (This is partial application. Example: f = (+) 3
is valid and it results in a function f
that adds 3 to whichever argument you pass to it, e.g. f 5 == 8
.)
Upvotes: 6
Reputation: 8467
As @Enlico has already said in their answer, the form .
without parentheses is an infix operator. By contrast, the form (.)
with parentheses refers to the function (.)
which takes two other functions as arguments and returns their composition.
To illustrate how this applies to your example, consider the four possible variations you might try:
map . foldr . id
map . foldr (.) id
map (.) foldr . id
map (.) foldr (.) id
These look pretty similar at first. But placing parentheses around the function applications reveals the differences:
map . foldr . id
map . (foldr (.) id)
(map (.) foldr) . id
(map (.) foldr (.) id)
In words: variation (1) runs id
, then applies foldr
to its result, then applies map
to the result of that. Evidently this is fairly useless, not least because composition with id
does nothing. Variation (2) is what you have: it folds a list using function composition — i.e. using the function (.) :: (b -> c) -> (a -> b) -> a -> c
— with a base case of id
, before passing the result of this as the first argument of map
, thereby mapping this newly composed function over a list. Variation (3), on the other hand, is not only meaningless but badly written to boot: not only does it evaluate map (.) foldr
, an expression which doesn’t even typecheck, it then attempts to compose it with id
, which as I already mentioned does nothing. Finally, variation (4) applies the function map
— which as we all know only takes two arguments at most — to four arguments, namely the functions (.)
, foldr
, (.)
and id
. Clearly, variation (2), which is what you have, is the only one here which makes sense.
Upvotes: 3