Road to Haskeller #6 - Composition

Last Edited: 6/18/2024

The blog post introduces a concept of composition, which is an important concept in functional programming including Haskell.

Haskell & Composition

Composition

Composition is when we take two functions and combine them to make a new function, h(x)=f(g(x)).In Haskell, composition can be achieved as follows:

( . ) :: (b -> c) -> (a -> b) -> a -> c
(f . g) === (\x -> f(g x))

How can this be useful? Let's look at a practical example.

descSort = reverse . sort

The above is a short way of combining functions to apply descending sorting. Also, we see that it is super useful for map.

map2D = map . map --- first map for iterating rows, second map for iterating elements in a row
map2D (\x -> 2*x) [[1,2,3],[2,3,4]] --- Output: [[2,4,6],[4,6,8]]

The above can achieve mapping on two-dimensional lists. We can build a mapping function for any dimensional array with n dimensions by using map . map for n times.

Dollar Sign ($)

When you have encountered a situation where you have so many functions in one line, you must have felt stressed about writing many parentheses. Luckily, Haskell has a way of simplifying syntax.

($) :: (a -> b) -> a -> b
f xs = map (\x -> x + 1) (filter (\x -> x > 1) xs)
f xs = map (\x -> x + 1) $ filter (\x -> x > 1) xs

Instead of writing parentheses, you can use $ to indicate that you will run another function.

(.) vs ($)

You might be confused about when to use . or $. In many cases, it is up to you. You just need to be aware that . combines two functions to make a new function, and $ is a syntax for applying two functions consecutively. The following example will hopefully clear things up if you are confused.

add1 x = x + 1
add2 x = x + 2
 
--- For .
add3 = add1 . add2 --- VALID (x+2)+1
add3 x = add1 . add2 x --- INVALID! cannot combine a function and a value (result of add2 x) with .!
 
--- For $
add3 = add1 $ add2 --- INVALID! add1 only takes Num as input, not a function
add3 x = add1 $ add2 x --- VALID y=(x+2), y+1=result. 

Personally, I prefer using . when I build reusable functions and using $ when I apply that combination only once.

Exercises

This is an exercise section where you can test your understanding of the material introduced in the article. I highly recommend solving these questions by yourself after reading the main part of the article. You can click on each question to see its answer.

Resources