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:
- every error, default case, and unexpected state is explicitly handled.
- parallelizing tasks only requires adding a single line.
- every function can easily be factored out into smaller components.
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.
- A function stores knowledge of how to perform some task.
- Combining functions in various ways (e.g. in sequence or in parallel) lets you create new functions that perform more complicated tasks
- 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
- 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.
- 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:
- functions that take more than one parameter
- side effects that donāt return a value, like console.log
š 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.