# Understanding function composition

The foundation of programming is to take smaller abstractions and make bigger abstractions out of it. In the previous articles in this series, we learned about using functions to create abstractions. Now let us look at how to join them up together using functional composition.

In the previous articles in this series, we learnt about using functions to create abstractions. Now let us look at how to join them up together using functional composition.

We start our example with these three functions

```
def twice(n):
return 2 * n
def square(n):
return n * n
def half(n):
return n / 2
```

We now want to create a function that can calculate `(n * n)/2`

. We already have a function `square`

and a function `half`

with us. We need to create a function that is the combination of these two functions.

Let us create a function that can combine any two functions. This operation is called function composition.

## Function Composition

At this point, you might stop me and say why not just write a new function like this

```
def square_and_half(n):
return half(square(n))
```

The problem is that this function is hardcoded to do `square`

followed by `half`

. We will have to write a separate function if we want to combine `twice`

and `square`

. And what if we want the user to select the functions and then combine them dynamically at runtime?

For these reasons, let us create a separate function to do this composition. You probably can guess the approach – higher order functions. Here is the `compose`

function

```
def compose(first_fn, second_fn):
def composed_fn(*args, **kwargs):
return second_fn(first_fn(*args, **kwargs))
return composed_fn
```

The `compose`

function takes two other functions as input, and returns a function as output. The output function will execute the two input functions one after the other.

```
fn = compose(square, half)
fn(4) # 8
fn = compose(twice, square)
fn(4) # 64
```

As we can see above, we can combine any two functions and get a composed function as output.

You can extend this to a chain of more functions. If we want to do `twice -> square -> half`

then we do this

```
fn = compose(compose(twice, square), half)
fn(4) # 32
```

Of course, it would be much nicer to extend the implementation of `compose`

to accept any number of input functions, so that one could just do `compose(twice, square, half)`

. That is a fun exercise to try as a homework 😊

## Composing functions with many inputs

All the functions `twice`

, `square`

and `half`

are unary – they all take only a single input. So the output of `twice`

can go as in input to `square`

, and the output of `square`

can go as an input to `half`

.

What if the functions are not unary? The `add`

function below is a binary function (binary – takes 2 inputs)

```
def add(a, b):
return a + b
```

We now want to create a function to evaluate the expression `(5 + 2*n)/2`

. We can easily calculate `2*n`

using the `twice`

function, and the output of that needs to go as an input to `add`

. The problem is, `add`

takes two inputs, not one. The second input to `add`

is `5`

. How do we compose it?

Take a moment to think about this before proceeding further.

The answer is to use partial application that we discussed in the article on higher order functions. Using `partial`

, we will partially apply the `5`

parameter to the `add`

function.

```
from functools import partial
add5 = partial(add, 5)
```

Now we have a function that takes a single parameter, and we can compose it easily

```
fn = compose(compose(twice, add5), half)
fn(4) # 6.5
```

And that's it. Using these techniques, you can compose any number of functions taking any number of parameters.

## Abstraction and composition

Composition is really a simple topic, but it is very important as this is the glue that allows us to combine functions together to build bigger and bigger abstractions.

Think about it like lego bricks, the holes that allow the bricks to fit together look simple, but are so important. Once you have that, you can take basic bricks and fit them together to make walls, take walls and fit them to make buildings, take buildings and make streets, take streets and make cities...

When you are making a wall, you need to think about where every brick goes. When making the building you are thinking "I need a wall here, and a wall here and a roof here, put a door here and some pillars besides the door". So you are not thinking about individual bricks anymore, you are thinking in terms of walls, roof, door and pillars. And when laying out the city you might be thinking, "this building goes here, there is a garden around it, and roads around it. The second building goes on the other side of the road...". so now the thinking is at an even higher level language of buildings, gardens, and roads.

That is essentially abstraction in action, and what is true for lego is the same for programming. Our goal is to create small abstractions, combine them into bigger abstractions, then combine those into still bigger abstractions and so on until we get our final program. Each level of abstraction provides us a certain "language" of thinking which we can use to solve bigger problems. And composition is the glue that sticks all this together.

In the next two articles, we will take all that we have learnt so far and put it together to solve some problems.