Understanding function composition and partial application

Independent consultant and developer Chip Camden uses practical examples to explain two principles of Functional Programming.

Let's say that we have a list (or array) of temperatures in Celsius, and we want to create a new list containing the same temperatures expressed in Fahrenheit. In most imperative programming languages, this kind of operation calls for a loop. For instance, in C# you might do something like this:

List<float> ctemps = new List<float>() {37, -5, 0, 48};

List<float> ftemps = new List<float>();

foreach (float temp in ctemps)


ftemps.Add((float)(temp * 1.8 + 32));


Languages that support a Functional Programming style provide a higher-order function, usually called "map," that returns a new list generated by mapping a function to each of the elements of an existing list. Thus, for example, in Ruby we could rewrite the above as:

ctemps = [37, -5, 0, 48]

ftemps = {|temp| temp * 1.8 + 32}

The map function requires two parameters: the list on which to operate (ctemps in the above example) and the function to map over each of its elements (the code block in braces above). This arrangement is more obvious in a pure functional language like Haskell:

let ctemps = [37.0, -5, 0, 48]

in map (\ x -> x * 1.8 + 32) ctemps

The expression in parentheses is a lambda, which corresponds to the code block given in the Ruby example. The backslash stands for the Greek character lambda (λ), and x is the assigned name for its parameter. We could have passed a named function instead of the lambda; the only reason why we used a lambda is because we don't have a named function that multiplies its parameter by 1.8 and then adds 32. We could write one:

let ctemps = [37.0, -5, 0, 48]

toF x = x * 1.8 + 32

in map toF ctemps

But in pure functional languages every operation is a function, including multiplication and addition. All the toF function is really doing is combining these functions, along with some specific parameters, into one function. The rest of this post will explain that statement.

Partial function application

As I mentioned, in Haskell multiplication is a function, like everything else; it takes two numeric arguments and returns their product. But in Haskell, no function really has more than one parameter.

Those readers who are unfamiliar with Functional Programming are now trying to discern the sound of one hand clapping.

All functions in Haskell are curried, which means that when you call a function that takes two parameters, what that function returns is another function that takes the other parameter. For example, let's say I have the following function:

mymult :: Int -> Int -> Int

mymult x y = x * y

This little function is a simplified version of multiplication, but it will serve to illustrate the point. The first line is our type signature; it says that this function takes an Int (integer) argument and returns a function that takes an Int argument and returns an Int. If we invoke it:

mymult 2 3

The expression mymult 2 returns a function that then takes 3 as an argument to invoke the rule that we've established. Why is this important? Because we can break that apart:

let mymult2 = mymult 2

in map mymult2 somelist

The map function expects as a parameter a function that takes only one parameter. Since mymult takes two parameters, we can partially apply it by supplying one of the arguments in order to create a function that we can pass to map. We don't even need to name it:

map (mymult 2) somelist

Better yet, we can ditch our custom function and just partially apply the multiplication operator:

map (2*) somelist

Now, why did I put the '*' after the 2? In Haskell, functions that are named with special symbols are infix by default. Haskell has a syntax for calling them in prefix fashion, as well as for calling prefix functions with infix notation, but we don't need to explore that here. Since multiplication is commutative, I could have said:

map (*2) somelist

If the operation were division or subtraction, though, then the order would matter.

Function composition

How does this help our little toF function? We've seen how we can pass a partially-applied binary operation to map, but in our case we need two operations: multiplication by 1.8 and addition of 32. We want to take the output of the first operation, and pipe it to the second one to get our result.

Well, that's what the "function composition" operator (.) is for. Joining two functions with a "." creates a new function that first invokes the right-hand function, then passes its result to the left-hand function. Thus, we can eliminate our toF function altogether:

let ctemps = [37.0, -5, 0, 48]

in map ((+32) . (*1.8)) ctemps

We can also compose two other functions, show (convert to String) and putStrLn (output to the terminal), to see our result:

main = do

putStrLn . show $

let ctemps = [37.0, -5, 0, 48]

in map ((+32) . (*1.8)) ctemps

We could have written that as putStrLn(show(...)). That's effectively the same thing, because we're evaluating it right away. In the case of map, though, as with all higher-order functions, we're building a pipeline that will be applied to values to be supplied at a later point. In that case, direct application of the result of the first function won't work:

map ((+32) (*1.8)) ctemps

That's wrong, because (*1.8) returns a function, and (+32) is expecting a number. You have to use function composition to get this to work.


If you've never been exposed to Functional Programming before, you may be experiencing a bit of vertigo, nausea, and numbness in the extremities that's a normal part of learning Haskell. There will be more, if you continue. But hopefully these disorienting episodes will lead to moments of enlightenment that will not only enable you to become proficient in Functional Programming, but will also make you a better programmer in any language.


Chip Camden has been programming since 1978, and he's still not done. An independent consultant since 1991, Chip specializes in software development tools, languages, and migration to new technology. Besides writing for TechRepublic's IT Consultant b...

Editor's Picks