Currying and Function Composition with Typescript

Oscar
3 min readJun 16, 2022

Now that we have gone through what a Pure Function is, we can start looking at what can be achieved by playing around with functions and context.

To start, let’s talk about Currying.

The definition from Wikipedia is:

In mathematics and computer science, currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each takes a single argument.

So basically a curried function is a function in which the return value is another function.

A simple example would be updating the following sum code:

const sum = (a: number, b: number) => a + b;const result = sum(10, 20);

To:

const currySum = (a: number) => (b: number) => a + b;const result = currySum(10)(20);

In which the sumfunction returns a secondary function instead of having the two parameters inline and returning the result right away.

When and why is it good to use currying?

This technique is especially powerful when we want to provide context to a function that will be executed later on where the information is not going to be available.

Let's take a look at a real-life example by using React by building a form that captures the user's basic information.

The easiest way to accomplish this is by having an onChange callback that would update the internal state every time the user types something.

The problem is that this is not as DRY as we could possibly implement it. Using currying we can achieve a simpler and cleaner solution.

By creating the handleChange function which returns the onChange callback we can save some steps as all of the updates will be handled by the same logic.

This is a very commonly used technique in Functional Programming and it's key to unlocking function composition and High Order Functions.

Now let's talk about Function Composition.

Composing functions is the technique of aggregating simple functions to create more robust ones. Same as currying, the final result of composing a function is another function.

A simple example would be by having a sumand aproductfunction and we want them to be executed one after the other. We can combine them together so the result function is always ready to be used later on.

const sum = (a: number) => a + 1;
const product = (a: number) => a * 2;
const composed = (a: number) => product(sum(a));composed(1);
// 4

With Typescript, we can build a function to compose a n number of functions by passing them as parameters. The function looks like this:

const compose = <T>(...functionList: Function[]) => (value: T) => functionList.reduceRight((result, fn) => fn(result), value);

This results in a clearer way of aggregating the two functions we want to be executed one after the other.

const composed = compose<number>(product, sum);composed(1);
// 4

The upright most function will be executed first, in this case, the sum will be calculated before the product .

Now that we learned the basics around currying and function composition we can up the level a little bit. In the following code snippet, we have a scenario where we want to update the user information that is divided into three types (info, address, and contact).

A way we can execute the update functionality without what we learned is by wrapping every result into the next function.

Ugly right? And if we keep adding functions it will become harder to read. So let’s enhance it with what we learned.

Once we create the composeUpdate function by composition we can use it to trigger the same update on multiple user objects with the predefined information like:

const user1: TUser = {...};
const user2: TUser = {...};
const updatedUser1 = composedUpdate(user1);
const updatedUser2 = composedUpdate(user2);

Conclusion

  • Currying is a useful technique to share context among multiple levels of functions.
  • Function composition eases the process of chaining multiple processes that depend on the result of the previous step.
  • The idea of composing functions can be described as combining multiple curried functions to create a more complex one that will be executed later.

This blog post is part of the Functional Programming with Typescript series on how to integrate functional programming principles for your application while keeping it type-safe with Typescript.

Introduction | Pev < | Next >

--

--