Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Boxed: bringing algebraic types to TypeScript
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Matthias Le Brun
March 27, 2024
Technology
180
0
Share
Boxed: bringing algebraic types to TypeScript
Matthias Le Brun
March 27, 2024
More Decks by Matthias Le Brun
See All by Matthias Le Brun
GraphQL, Pothos & SQLite: a perfect match
bloodyowl
0
110
(why the hell did I) build a GraphQL client for the browser
bloodyowl
0
150
leveraging (algebraic data) types to make your UI rock @ jsheroes
bloodyowl
0
330
Leveraging (algebraic data) types to make your UI rock solid
bloodyowl
0
490
La drôle d'histoire de JavaScript
bloodyowl
0
390
Healthy Code Collaboration
bloodyowl
0
360
Simplify your UI management with (algebraic data) types
bloodyowl
0
850
Simplify your UI management with (algebraic data) types
bloodyowl
1
570
Migrating a large Reason+React codebase to hooks
bloodyowl
0
600
Other Decks in Technology
See All in Technology
サイバーセキュリティ概論 / Introduction to Cybersecurity
ks91
PRO
0
120
電子辞書Brainをネットに繋げてみた(自力編)
raspython3
0
420
OCI Oracle AI Database Services新機能アップデート(2026/03-2026/05)
oracle4engineer
PRO
0
120
美味しいスイスチーズを作ろう🧀🐭
taigamikami
1
220
Chart.js が簡単に使えるようになっていたので OGP 画像生成に使った話
kamekyame
0
130
Dynamic Workersについて
yusukebe
2
570
脅威をエンジニアリングの糧にして:恐怖を乗り越えた先にあったもの / Turn threats into fuel for engineering: what lay beyond overcoming fear
nrslib
1
370
PHP と TypeScript の型システム比較:AI 時代の「型」は誰のためにあるのか? #frontend_phpcon_do / frontend_phpcon_do_2026
shogogg
1
240
トークン数だけでは測れない — Claude Code 組織展開の効果検証から学んだこと
makikub
0
110
Java正規表現エンジン(NFA)の仕組みと パフォーマンスを維持するための最適化手法
takeuchi_132917
0
170
TROCCOで始めるクラウドコストを民主化するためのFinOps
tk3fftk
3
550
さきさん文庫の書籍ができるまで
sakiengineer
0
330
Featured
See All Featured
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.8k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2k
WCS-LA-2024
lcolladotor
0
610
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.2k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.1k
The Cost Of JavaScript in 2023
addyosmani
55
10k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
150
Why Our Code Smells
bkeepers
PRO
340
58k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
Color Theory Basics | Prateek | Gurzu
gurzu
0
320
Transcript
Boxed
Matthias Le Brun @bloodyowl lead engineering manager chief shitpost office
the easiest way to provide banking features (accounts, payments, cards…) we're hiring! @
None
None
None
None
PREVIOUSLY ON Matthias tries to convert people to FP
Algebraic Data Types yay
type State = { data?: Data; error?: Error; };
type State = { data?: Data; error?: Error; }; type
Result<Data> = | Ok<Data> | Error<Error>; error data ERROR NULL NULL DATA NULL NULL ERROR DATA
{ data: null; error: null; };
type Result<Data> = | Ok<Data> | Error<Error>;
type State = { data?: Data; error?: Error; }; type
Result<Data> = | Ok<Data> | Error<Error>; state ERROR DATA
API response API response
API response API response
type State = { data?: Data; error?: Error; }; type
Result<Data> = | Ok<Data> | Error<Error>; 2 2 1 1 4 2 x = + =
Algebra it's just basic math™
+ x
quiz 157 x 8341 = ?
quiz 999 + 1 = ?
union types
monads functors
functor
monad
[1, 2, 3].map(x => x * 2) // [2, 4,
6] [1, 2, 3].flatMap(x => [x, x * 2]) // [1, 2, 2, 4, 3, 6]
map: <A, B>(T<A>, (a: A) => B) => T<B>: flatMap:
<A, B>(T<A>, (a: A) => T<B>) => T<B>;
None
null & undefined are bad
errors aren't always exceptions
promises were made the wrong way in JS
promises were made the wrong way in JS
promises were made the wrong way in JS
promises were made the wrong way in JS
THAT WAS Matthias tries to convert people to FP NOW
LET'S TALK ABOUT BOXED
✨ new job ✨ 2022
«we use TypeScript»
None
let's look at the ecosystem™
fp-ts
pipe( token, TaskEither.bindW("user", getTokenFromUser), TaskEither.bindW("project", getProject), TaskEither.chainW(getMembership), ) no autocomplete
on current value
pipe( token, TaskEither.bindW("user", getTokenFromUser), TaskEither.bindW("project", getProject), TaskEither.chainW(getMembership), x => {}
) need to inspect argument for type
pipe( token, TaskEither.bindW("user", getTokenFromUser), TaskEither.bindW("project", getProject), TaskEither.chainW(getMembership), x => {}
) need to inspect argument for type not a good DX
export declare const chainFirstTaskK: <A, B>(f: (a: A) = >
T.Task<B>) = > <E>(first: TaskEither<E, A>) = > TaskEither<E, A> ah just what I was looking for
we've looked at the ecosystem™
«let's try to make these types work with a good
DX in a structurally typed language» — me, allegedly drunk
structural typing
class Some<A> {} class None {} const value: Some<string> =
new None();
what we need → good DX → interop with ts-pattern
→ fast
what we needed → chaining API → autocomplete → less
imports
1st iteration: naive wanted the types to be opaque but
safe
1st iteration: naive discriminator in TS (aka poor man's nominal
types)
1st iteration: naive and we're storing the state in the
class instance
None
1st iteration: naive wanted the types to be opaque but
safe
1st iteration: naive wanted the types to be opaque but
safe
2nd iteration: anger
2nd iteration: anger
2nd iteration: anger
2nd iteration: anger
3rd iteration: make it fast
initial release
initial release
type Option<A> = Some<A> | None;
type Result<A, E> = Ok<A> | Error<E>;
type AsyncData<A> = | NotAsked | Loading | Done<A>;
Future<A>; better promises™ cancellable monadic leaves error state to Result
const parsed = input != null ? parseInput(input) : undefined;
const transformed = parsed != null ? transform(parsed) : undefined; const printed = transformed != null ? print(transformed) : undefined; const value = printed != null ? prettify(printed) : "fallback"; input .map(parseInput) .flatMap(transform) .map(print) .map(prettify) .getWithDefault("fallback");
user-testing
None
insights people need human-written docs → we doubled down on
examples & added search
insights don't try to replicate another language → give good
defaults for TS
Result< Data, ServerError > Result< A, ServerError | FinalizeError >
insights people are familiar with just a few data types
→ reduce confusion
insights people can be docs-first or type-first → experience needs
to be good for both
insights readability matters → chaining API was the way to
go
insights mapping on existing JS knowledge helps → if it
exists, name it the same way
insights pipe() syntax had impact on the runtime → expressiveness
is important
4th iteration implement the feedback rename some methods update some
defaults
5th iteration: what the actual fu
5th iteration: what the actual fu
recently fix the errors
recently improved perf by 2x to 10x
recently improved perf by 2x to 10x blazing fast certi
fi ed™ ⚡
Boxed → github.com/swan-io/boxed → swan-io.github.io/boxed
None
$ yarn add @swan-io/boxed
Matthias Le Brun @bloodyowl we're still hiring thank you! 🙏