Jiaqi Shao
Jiaqi Shao

Reputation: 55

What's the difference between . and (.) in Haskell?

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

Answers (2)

Enlico
Enlico

Reputation: 28366

The difference between . and (.) is the same as that between + and (+), - and (-), and all the others:

  • without parenthesis you have the infix form, for which you put the operands right before and after the symbol, as in f . g or 3 + 5;
  • with the parenthesis you have the function application form, for which the operands go both after the function name, as in (.) 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

bradrn
bradrn

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:

  1. map . foldr . id
  2. map . foldr (.) id
  3. map (.) foldr . id
  4. map (.) foldr (.) id

These look pretty similar at first. But placing parentheses around the function applications reveals the differences:

  1. map . foldr . id
  2. map . (foldr (.) id)
  3. (map (.) foldr) . id
  4. (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

Related Questions