Notations for function application and composition in Haskell

I am going to cover the three notations, the three ways that function application and composition can be expressed in Haskell. These are i) parentheses, ii) the \$ operator and iii) the dot (.) operator.

To study these notations, let me introduce a simple example, irrelevant to everything. Fire up WinGHCi and, at the Prelude> prompt, type

import Data.List

so that we can work with every function that applies to lists. The Prelude> prompt now changes to Prelude Data.List>.

Now type

[[a] ++ [b] | a <- [1..3], b <- [4..6]]

at the prompt. This is a list comprehension. This particular list comprehension is a list of lists. Each of the inner lists has two elements: the first is an integer from 1 to 3 and the second is an integer from 4 to 6. WinGHCi responds by producing the whole list:

[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]

Now there is a function in Haskell called concat which eliminates the inner square brackets. Type

concat [[a] ++ [b] | a <- [1..3], b <- [4..6]]

and WinGHCi responds by outputting the result:

[1,4,1,5,1,6,2,4,2,5,2,6,3,4,3,5,3,6]

There is another function in Haskell called nub, that eliminates all duplicate elements from a list. To have nub operate on the output of concat we type:

nub(concat [[a] ++ [b] | a <- [1..3], b <- [4..6]])

and WinGHCi responds by outputting the result:

[1,4,5,6,2,3]

There is yet another function in Haskell called sort that sorts  the elements of a list. To have sort operate on the output of nub we type:

sort(nub(concat [[a] ++ [b] | a <- [1..3], b <- [4..6]]))

and WinGHCi responds by outputting the result:

[1,2,3,4,5,6]

Did you like my example? I used three functions (concat, nub and sort) one after the other, in order to compute a sorted list of the unique elements of a list of lists.

And here we can see the first way, the first notation that we can use in Haskell in order to apply one function after another, or to phrase it equivalently, to compose functions. And this notation is the use of parentheses.

Now, parentheses are great, at least for me, but some people do not like them, especially when there are a lot of functions involved in a single statement. LISP uses a lot of parentheses, and this has caused people to mock LISP, ironically stating that LISP stands not for “LISt Processing” but for “Lots of Inane Silly Parentheses”. The creators of Haskell knew that and also wanted to provide a language with a concise, succinct and laconic syntax. Thus they coined the \$ operator which can substitute a pair of parentheses. Thus, with the use of the \$ operator, the previous statement becomes:

sort \$ nub \$ concat [[a] ++ [b] | a <- [1..3], b <- [4..6]]

The \$ operator is the second notation that can be used when we apply and compose functions. What the \$ operator does is that it changes the associativity of function application in Haskell. Normally function application with the use of spaces is left-associative, whereas function application with \$ is right-associative. In our imagination, we can “replace” the \$ operator with a left parenthesis where it exists and “add” a right parenthesis at the end of the statement.

But there is also a third notation, straight from Mathematics: the function composition operator. In Mathematics, the composition operator is denoted with a small circle. Since there is not a small circle symbol in the keyboard, the creators of Haskell chose something similar: the dot. Thus, since we compose these three functions, we have equivalently:

(sort . nub . concat) [[a] ++ [b] | a <- [1..3], b <- [4..6]]

To recapitulate, the following four(!, the fourth is a bonus!) statements are equivalent:

sort(nub(concat [[a] ++ [b] | a <- [1..3], b <- [4..6]]))
sort \$ nub \$ concat [[a] ++ [b] | a <- [1..3], b <- [4..6]]
(sort . nub . concat) [[a] ++ [b] | a <- [1..3], b <- [4..6]]
sort . nub . concat \$ [[a] ++ [b] | a <- [1..3], b <- [4..6]] 