Practical Guide to Fp-ts P1: Pipe and Flow
Tags: functional, fpts, typescript
Published: 2020-06-16 00:14:12

---
title: Practical Guide to Fp-ts P1: Pipe and Flow
description: Learn how to use Fp-ts practically. Introduces the pipe and flow operators. No mathematical knowledge required.
tags: fp, fpts, typescript
canonical_url: https://rlee.dev/practical-guide-to-fp-ts-part-1
series: Practical Guide to Fp‑ts
---
## Introduction

This post is an introduction to [fp-ts](https://github.com/gcanti/fp-ts), a functional programming library for Typescript. Why should you be learning fp-ts? The first reason is better type safety. Fp&#8209;ts allows you to make assertions about your data structures without writing [user-defined type guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) or using the `as` operator. The second reason is expressiveness and readability. Fp-ts gives you the tools necessary to elegantly model a sequence of operations that can fail. All in all, you should add fp-ts to your repertoire of tools because it will help you write better Typescript programs.

Contrary to popular opinion, you don’t need to understand complex mathematics to learn functional programming. In truth, you just need to get a feel for how each operator works. Once you get a handle on the basic operators, you can go back and review the mathematics. With this in mind, this post and the ones that follow, I will introduce functional programming from a practical perspective, avoiding mathematical jargon unless necessary.

## The Pipe Operator

The basic building block of fp-ts is the [Pipe](https://gcanti.github.io/fp-ts/modules/function.ts.html#pipe) operator. Intuitively, you can use the operator to chain a sequence of functions from left-to-right. The type definition of `pipe` takes an arbitrary number of arguments. The first argument can be any arbitrary value and subsequent arguments must be functions of [arity](https://en.wikipedia.org/wiki/Arity) one. The return type of a preceding function in the pipeline must match the input type of the subsequent function.

Let's look at how to pipe simple addition and multiplication functions.

```ts
import { pipe } from 'fp-ts/lib/function'

function add1(num: number): number {
 return num + 1
}

function multiply2(num: number): number {
 return num * 2
}

pipe(1, add1, multiply2) // 4
```

The result of this operation is `4`. How did we arrive at this result? Let’s look at the steps.

1. We start with the value of `1`.
2. `1` is piped into the first argument of `add1` and `add1` is evaluated to `2` by adding `1`.
3. The return value of `add1`, `2` is piped into the first argument `multiply2` and is evaluated to `4` by multiplying by `2`.

Currently our pipeline inputs a number and outputs a new number. Is it possible to transform the input type to another type, like a `string`? The answer is yes. Let’s add a `toString` function at the end of the pipeline.

```ts
function toString(num: number): string {
 return `${num}`
}

pipe(1, add1, multiply2, toString) // '4'
```

Now our pipeline evaluates to `’4’`. What happens if we were to put `toString` between `add1` and `multiply2`? We get a compile error because the output type of `toString`, `string` does not match the input type of `multiply2`, `number`.

```ts
pipe(1, add1, toString, multiply2)
```

> Argument of type '(num: number) => string' is not assignable to parameter of type '(b: number) => number'.
  Type 'string' is not assignable to type 'number'.ts (2345)

In short, you can use the `pipe` operator to transform any value using a sequence of functions. The flow of control can be modelled as follows. [^1]

```java
A -> (A->B) -> (B->C) -> (C->D)
```

## The Flow Operator

The `flow` operator is almost analogous to the `pipe` operator. The difference being the first argument must be a function, rather than any arbitrary value, say a number. The first function is also allowed to have an arity of more than one.

For example, we could wrap our three functions inside of the `flow` operator.

```ts
import { flow } from 'fp-ts/lib/function'

pipe(1, flow(add1, multiply2, toString))
flow(add1, multiply2, toString)(1) // this is equivalent
```

In comparison, to the `pipe` operator, this is what the "flow" of control looks like for the `flow` operator. [^2]

```java
(A->B) -> (B->C) -> (C->D) -> (D->E)
```

What is a good use case for the `flow` operator? When should you use it over the `pipe` operator? A general rule of thumb is when you want to avoid using an anonymous function. In Typescript, a good example of an anonymous function are callbacks.

Lets declare a function, `concat` with arity of `2`. The first argument will be a number. The second argument is a callback that takes a number as its first argument and transforms it into a string. The function returns the first value and the transformed second value as a two dimensional [tuple](https://en.wikipedia.org/wiki/Tuple).

```ts
function concat(a: number, transformer: (a: number) => string): [number, string] {
  return [a, transformer(a)]
}
```

Using our previous repertoire of functions, we can compose a callback for this function. Here is an example using the `pipe` operator.

```ts
concat(1, (n) => pipe(n, add1, multiply2, toString)) // [1, '4']
```

What’s the problem with this? The problem is we have to declare `n` as part of an anonymous function to use it with the `pipe` operator. You should avoid this because it puts you at risk of shadowing a variable in the outer scope. It is also more verbose.

The solution is to use the `flow` operator and remove the anonymous function. This works because the return value of `flow` is a function itself. The signature of this function is `number -> string` which is exactly the same as the callback signature.

```ts
concat(1, flow(add1, multiply2, toString)) // [1, '4']
```

[^1]: [Pipe Typescript Definition](https://gcanti.github.io/fp-ts/modules/function.ts.html#pipe)
[^2]: [Flow Typescript Definition](https://gcanti.github.io/fp-ts/modules/function.ts.html#flow)