Candle logo
Avatar image for Liam Butler-Lawrence

By Liam Butler-Lawrence

How to save 100s of debugging hours with fp-ts

Part 1: combining functions in sequence with flow and pipe


šŸ§ Should I even care about functional programming?

How many hours have you spent debugging something and thinking ā€œhow did it even get into this state?ā€ Or ā€œhow did I miss that edge case?ā€ Or ā€œif only I could refactor thisā€¦ā€ But thatā€™s just wishful thinking, right?

Wellā€¦ no. With functional programming:

Picture your app or service as a corn maze. Using traditional programming is like trying to design it from inside the mazeā€”hacking away one step at a time.

But using functional programming is like designing it from an airplane, where you can see how each path connects to make a simple, clear whole, which does exactly what you want.

šŸ“š But donā€™t I need to learn Haskell or something?

Nope. fp-ts is an actively maintained TypeScript library with thousands of stars on GitHub.

It adds complete functional programming to TypeScript, using simple syntax youā€™re already familiar with.

Installing it couldnā€™t be simpler:

npm i fp-ts

āš”ļø But isnā€™t functional programming complicated?

It really isnā€™t! If you know how to write code, you can understand the core concepts behind functional programming.

  1. A function stores knowledge of how to perform some task.
  2. Combining functions in various ways (e.g. in sequence or in parallel) lets you create new functions that perform more complicated tasks
  3. Your entire app or service is really just one big function, made up of smaller functions combined in different ways

šŸ’”As simple as it getsā€¦

flow lets you combine functions by stringing them together in sequence. Whatever each function returns is passed as a parameter to the next function in line. Letā€™s imagine weā€™re writing a check to determine if we support shipping to a userā€™s address. Hereā€™s a traditional implementation.

const isSupportedAddress = (address: string) => {
  const parsedAddress = parseAddress(address)
  const countryCode = getISOCountryCode(parsedAddress.country)

  return ['US', 'MX', 'CA'].includes(countryCode)
}

Notice the unnecessary variables and redundant naming. And hereā€™s the same code using flow:

import { flow } from "fp-ts/function";

const isSupportedAddress = flow(
  parseAddress,
  (_) => _.country,
  getISOCountryCode,
  ["US", "MX", "CA"].includes
);

The actual functions weā€™re stringing together are front and center, with no extraneous information or syntax.

šŸŖ„ Syntax sugar

pipe is a version of flow that takes an extra first parameter, which it uses to call the combined function. So instead of returning a new function, pipe just returns a new value. Letā€™s say youā€™re working inside an existing function, and you want to use flow to simplify some logic, but the syntax ends up being a little hard to read:

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

const submitShippingOrder = (user: UserModel) => {

  const addressIsSupported = flow(
    parseAddress,
    _ => _.country,
    getISOCountryCode,
    ['US', 'MX', 'CA'].includes
  )(user.address)

  if (addressIsSupported) {
    // submit order
  }
}

Notice that weā€™re calling the combined function immediately after creating it, by passing it user.address. Instead, we can use pipe:

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

const submitShippingOrder = (user: UserModel) => {

  const addressIsSupported = pipe(user.address,
    parseAddress,
    _ => _.country,
    getISOCountryCode,
    ['US', 'MX', 'CA'].includes
  )


  if (addressIsSupported) {
    // submit order
  }
}

šŸ’Ŗ Some things you should know

  1. fp-ts is completely type-safe, so the return type of each function in a flow or pipe must match the parameter type of the next function.
  2. Each function in aflow or pipe must accept exactly one parameter and return exactly one value. But donā€™t worry! In future posts, weā€™ll learn how to include more kinds of functions, including:

šŸ˜€ Hope that helped!

But if you have any doubts, leave a comment and Iā€™ll get back to you :) And hit that subscribe button to catch my next post about functional programming in TypeScript.

Iā€™m Liam, the co-founder of Candle Finance. Weā€™re a Techstars-backed startup on a mission to make DIY investing safer and more automatic, so you can invest in what you believe inā€”and then go and live your life.


Get updates from us.