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
190
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
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
120
(why the hell did I) build a GraphQL client for the browser
bloodyowl
0
160
leveraging (algebraic data) types to make your UI rock @ jsheroes
bloodyowl
0
340
Leveraging (algebraic data) types to make your UI rock solid
bloodyowl
0
520
La drôle d'histoire de JavaScript
bloodyowl
0
410
Healthy Code Collaboration
bloodyowl
0
370
Simplify your UI management with (algebraic data) types
bloodyowl
0
860
Simplify your UI management with (algebraic data) types
bloodyowl
1
590
Migrating a large Reason+React codebase to hooks
bloodyowl
0
620
Other Decks in Technology
See All in Technology
変化の早いClaude Codeを 書籍に落とし込む
oikon48
7
1.3k
モバイルアプリ開発概論2026
recruitengineers
PRO
3
490
第3回しろおびセキュリティスポンサーセッション
log0417
0
160
AI駆動開発は個人技からチーム戦へ:組織でAIを使いこなすための実践設計
moongift
PRO
0
480
Service Connect 上のサービスに ECS Service の外側から到達できなかった話
ota1022
1
150
20260608_Codexの可能性_ノンプログラマー向け_大城追記
doradora09
PRO
0
780
SmartHR Engineering Team Deck
smarthr
0
790
研究開発部の紹介 / Sansan R&D Profile
sansan33
PRO
4
24k
強化学習「理論」入門
enakai00
3
3.6k
制約理論(ToC)入門 2026版
recruitengineers
PRO
6
2k
修正PRを食べてレビュースキルが賢くなる:Claude Codeによる自己改善サイクル
yuyaumetsu
5
1.4k
モダンフロントエンド 開発研修
recruitengineers
PRO
3
550
Featured
See All Featured
Building Adaptive Systems
keathley
44
3.2k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
460
Fashionably flexible responsive web design (full day workshop)
malarkey
408
67k
The Pragmatic Product Professional
lauravandoore
37
7.4k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
3
410
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
630
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.5k
Balancing Empowerment & Direction
lara
6
1.2k
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
350
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
980
Building an army of robots
kneath
306
46k
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! 🙏