Function Composition
Observe that curry
and uncurry
are functions that produce new functions from
existing ones. The transformation is fairly straightforward—all we do is change
how the arguments are presented to the functions—but still, the new function is
not the same as the original function by virtue of taking different argument
types. What other transformations can we apply to build new functions from
existing ones?
Probably the most fundamental one, already discussed
here, is function
composition. Given two functions f :: a -> b
and g :: b -> c
, we can
combine them to obtain a function h :: a -> c
. This function passes its
argument of type a
to f
and then produces its result of type c
by passing
the result of f
, which is of type b
, to g
. The operator we use to do
this is (.)
:
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(g . f) x = g (f x)
Given that functional programming is programming by function composition, this is a rather fundamental operator. Often, we compose functions without using this operator explicitly and by using local variables in a function definition instead. Nevertheless, it is useful to have an operator that allows us to express explicitly that some function is the composition of a series of functions. We'll encounter this operator in various projects throughout this book.