Michael McGreal

Michael McGreal

Remeda Pipe()

Functional programming in Typescript

Since ES5, Javascript has included some functional programming methods on the Array prototype of .map, .filter, .sort, and .reduce. These can be chained like:

1array.filter().map().sort()

Imagine if you had to call each of these functions separately and pass the output of one to another – it would get messy!

Can we achieve this same pattern in our own custom functions?

The problem

Often to help with readability, I will wrap common code into a named function. This keeps it semantically grouped, and allows code folding in VScode (without the #region syntax).

However, these functions then need to be invoked, and can become messy like:

1const doWork = async (input: string): number => input.length
2const result = await doWork('work')
3const processResult = (result: Awaited<ReturnType<typeof doWork>>) => {...}
4processResult(result)
5const cleanup = async () => {...}
6cleanup()

It works, but it can become hard to quickly see the flow of functions. Function invocations mixed with function declarations. Especially if some unrelated code is added between.

This is a perfect case for functional programming pipes!

Introdcing R.pipe()

It looks like this:

1import r * as R from 'remeda' // tree shaking supported!
2 
3// define the functions
4const doWork = async (input: string): number => {...}
5const processResult = (result: Awaited<ReturnType<typeof doWork>>) => {...}
6const cleanup = async () => {...}
7 
8// call them sequentially in a pipe!
9r.pipe(
10 await doWork('input'), // the result of this is passed to the next pipe item!
11 async (x) => processResult(x), // this pipe item receives the result as a callback, and passes it to it's funciton
12 async () => cleanup() // async and await supported inside the pipe!
13)

This is a simple contrived example, but in real-world cases, it can dramatically improve the readability of long unwiedly programs.

In this way, you can chain together your own functions.