In this article, we review fp-ts library in Hoppscotch codebase. We willl look at:
What is Hoppscotch?
What is fp-ts?
fp-ts usage in Hoppscotch CLI.
I study patterns used in an open source project found on Github Trending. For this week, I reviewed some parts of Hoppscotch codebase and wrote this article.
fp-ts provides developers with popular patterns and reliable abstractions from typed functional languages in TypeScript.
The goal of fp-ts is to empower developers to write pure FP apps and libraries built atop higher order abstractions. It includes the most popular data types, type classes, and abstractions from languages like Haskell, PureScript, and Scala.
Represents a value of one of two possible types (a disjoint union).
An instance of Either is either an instance of Left or Right.
A common use of Either is as an alternative to Option for dealing with possible missing values. In this usage, None is replaced with a Left which can contain useful information. Right takes the place of Some. Convention dictates that Left is used for failure and Right is used for success.
Below is an example copied from the documentation.
import * as E from 'fp-ts/Either'import { pipe } from 'fp-ts/function'const double = (n: number): number => n * 2export const imperative = (as: ReadonlyArray<number>): string => { const head = (as: ReadonlyArray<number>): number => { if (as.length === 0) { throw new Error('empty array') } return as[0] } const inverse = (n: number): number => { if (n === 0) { throw new Error('cannot divide by zero') } return 1 / n } try { return `Result is ${inverse(double(head(as)))}` } catch (err: any) { return `Error is ${err.message}` }}export const functional = (as: ReadonlyArray<number>): string => { const head = <A>(as: ReadonlyArray<A>): E.Either<string, A> => as.length === 0 ? E.left('empty array') : E.right(as[0]) const inverse = (n: number): E.Either<string, number> => (n === 0 ? E.left('cannot divide by zero') : E.right(1 / n)) return pipe( as, head, E.map(double), E.flatMap(inverse), E.match( (err) => `Error is ${err}`, // onLeft handler (head) => `Result is ${head}` // onRight handler ) )}assert.deepStrictEqual(imperative([1, 2, 3]), functional([1, 2, 3]))assert.deepStrictEqual(imperative([]), functional([]))assert.deepStrictEqual(imperative([0]), functional([0]))
However, I did not see any reference to left or right as demonstrated above but instead I saw .toError being called as shown below:
I spent 200+ hours analyzing Supabase, shadcn/ui, LobeChat. Found the patterns that separate AI slop from production code. Stop refactoring AI slop. Start with proven patterns. Check out production-grade projects at thinkthroo.com