## DEV Community

Attila Večerek

Posted on • Updated on

# Function composition

Function composition is just a fancy term describing the act of passing the return value of one function as an argument to another. We say that functions compose well if the result of one function can be directly passed as an argument to another function.

``````// Good example
const toNumber = (a: string): number => Number(a);
const double = (a: number): number => a * 2;
const toArray = <A>(a: A): A[] => [a];

export const result = toArray(double(toNumber("21")));

export const badMultiply = (input: number, by: number): number =>
input * by;

export const result2 = toArray(badMultiply(toNumber("21"), 2));
``````

In the first example, the functions compose well because the result of each function matches the expected input of the next function. In the second example, it is no longer enough to just pass down the result of `toNumber` because `badMultiply` expects two arguments.

In functional programming, we strive to write functions that compose well. In TypeScript, it is not possible to return multiple values. Hence, the rule of thumb is to write functions of arity 1, i.e. functions that take a single argument. However, it is possible to "rewrite" functions so they do compose well using a technique called currying.

Currying is a transformation of functions that translates a function from callable as `f(a, b, c)` into callable as `f(a)(b)(c)`

``````export const multiply = (by: number) => (input: number) =>
``````

The above function takes a single argument and returns another function that also takes a single argument. For the sake of simplicity, going forward we refer to this as a function with two arguments. The order of arguments matters. The section called pipe provides more details on that.

Another technique worth mentioning is partial application. If we have a function of arity `n` and partially apply the first `m` arguments, the result is a function of arity `n - m`. This is extremely helpful for creating specialized functions that hold a certain state. For example:

``````// http.ts
type Method = "GET" | "POST" | "PUT" | "DELETE";
type Body =
| Blob
| Buffer
| URLSearchParams
| FormData
| string
| null
| undefined;
type Request = {
method: Method;
url: URL;
body: Body;
};

// a function of arity 2
export const request =
(method: Method) =>
(url: URL): Request => ({
method,
url: url,
body: undefined,
});

// functions of arity 1
export const get = request("GET");
export const post = request("POST");
export const put = request("PUT");
export const del = request("DELETE");
``````

In the above example, `request` is a generic request building function of arity 2. The two arguments it takes are `method` and `url`. We can partially apply every distinct value of `method` to the `request` function resulting in four specialized request functions, each of arity 1. Now, whenever we want to build a `get` request we have a shortcut to do so:

``````import { get, request } from "./http.js";

export const myGetRequest = request("GET")(new URL("http://google.com"));

// we can just do
export const myGetRequest2 = get(new URL("http://google.com"));
``````

## `pipe`

Code such as `toArray(double(toNumber("21")))` reads opposite to the order of execution but can still be considered as fairly readable. Now, imagine our program consisted of 10 or more functions. Code written in such style could fairly quickly become unreadable. `fp-ts` provides some tools to make function composition more readable. The example could be re-written using `pipe` the following way:

``````import { pipe } from "fp-ts/lib/function.js";

const toNumber = (a: string): number => Number(a);
const double = (a: number): number => a * 2;
const toArray = <A>(a: A): A[] => [a];

export const result = pipe(
"21",
toNumber,
double,
toArray
);
``````

The above code reads in the same order it is executed. It demonstrates `pipe` with functions that all expect a single argument. What about functions that take more arguments?

``````import { pipe } from "fp-ts/lib/function.js";

const toNumber = (a: string): number => Number(a);
const multiplyBy = (by: number) => (input: number) => input * by;
const toArray = <A>(a: A): A[] => [a];

export const result = pipe(
"21",
toNumber,
multiplyBy(2),
toArray
);
``````

The previous section mentions that the order of arguments in function composition matters. We can see why in the above example. If `input` was the first argument, the above code would have to be written as follows:

``````import { pipe } from "fp-ts/lib/function.js";

const toNumber = (a: string): number => Number(a);
const multiplyBy = (input: number) => (by: number) => input * by;
const toArray = <A>(a: A): A[] => [a];

export const result = pipe(
"21",
toNumber,
multiplyBy,
(by) => by(2),
toArray
);
``````

The above code is not only longer but it also reads bad. This is how much impact the order of arguments can have on writing functional code.

## `apply`

If the order of arguments prevents the function being simply piped into another and we don't want to wrap it, we could use `apply`. The previous code example could be rewritten as:

``````import { apply, pipe } from "fp-ts/lib/function.js";

const toNumber = (a: string): number => Number(a);
const multiplyBy = (input: number) => (by: number) => input * by;
const toArray = <A>(a: A): A[] => [a];

export const result = pipe(
"21",
toNumber,
multiplyBy,
apply(2),
toArray
);
``````

## `flow`

What if we wanted to reuse the code in `pipe` and call the function `calculate`?

``````import { pipe } from "fp-ts/lib/function.js";

const toNumber = (a: string): number => Number(a);
const multiplyBy = (by: number) => (input: number) => input * by;
const toArray = <A>(a: A): A[] => [a];

const calculate = (a: string) => pipe(
a,
toNumber,
multiplyBy(2),
toArray
);

export const result = calculate("21");
``````

The code does not read terribly bad but `fp-ts` provides another tool to solve the problem of code-reuse more elegantly. We can think of `flow` as a reusable `pipe`:

``````import { flow } from "fp-ts/lib/function.js";

const toNumber = (a: string): number => Number(a);
const double = (a: number): number => a * 2;
const toArray = <A>(a: A): A[] => [a];

const calculate = flow(toNumber, double, toArray);

export const result = calculate("21");
``````

`pipe`'s first argument is always a value and is followed by functions and returns a value. `flow` expects only function arguments and returns a function. The type annotation of `calculate` from the above example could be written as:

``````export type Calculate = (_: string) => number[];
``````

## Wrap-up

• Function composition is all about passing the result of one function to another as its input.
• In TypeScript, two functions compose well when they accept a single argument and their respective "input" and "output" types match.
• Functions that don't compose well can be transformed into functions that do by currying.
• Partial application allows us to create functions that store state and thus become specialized versions of a generic function that we partially applied the arguments on.
• `pipe` allows us to read composed functions in the order of execution.
• `apply` allows us to partially apply an argument to a function or work around suboptimal ordering of function arguments.
• `flow` allows us to re-use composed functions more easily.

Next up, we look into the commonly misunderstood concepts of functor and monad.