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
Matthias Le Brun
March 27, 2024
Technology
0
110
Boxed: bringing algebraic types to TypeScript
Matthias Le Brun
March 27, 2024
Tweet
Share
More Decks by Matthias Le Brun
See All by Matthias Le Brun
(why the hell did I) build a GraphQL client for the browser
bloodyowl
0
85
leveraging (algebraic data) types to make your UI rock @ jsheroes
bloodyowl
0
290
Leveraging (algebraic data) types to make your UI rock solid
bloodyowl
0
400
La drôle d'histoire de JavaScript
bloodyowl
0
360
Healthy Code Collaboration
bloodyowl
0
270
Simplify your UI management with (algebraic data) types
bloodyowl
0
810
Simplify your UI management with (algebraic data) types
bloodyowl
1
530
Migrating a large Reason+React codebase to hooks
bloodyowl
0
550
Third Party Hell (BestOfWeb)
bloodyowl
0
620
Other Decks in Technology
See All in Technology
「AI駆動開発」のボトルネック『言語化』を効率化するには
taniiicom
1
220
興味の胞子を育て 業務と技術に広がる”きのこ力”
fumiyasac0921
0
300
2025/07/22_家族アルバム みてねのCRE における生成AI活用事例
masartz
2
150
解消したはずが…技術と人間のエラーが交錯する恐怖体験
lamaglama39
0
140
SAE J1939シミュレーション環境構築
daikiokazaki
1
190
怖くない!GritQLでBiomeプラグインを作ろうよ
pal4de
1
140
AWS表彰プログラムとキャリアについて
naoki_0531
1
140
LLM開発を支えるエヌビディアの生成AIエコシステム
acceleratedmu3n
0
340
少人数でも回る! DevinとPlaybookで支える運用改善
ishikawa_pro
4
1.8k
ML Pipelineの開発と運用を OpenTelemetryで繋ぐ @ OpenTelemetry Meetup 2025-07
getty708
0
320
Kiroから考える AIコーディングツールの潮流
s4yuba
1
430
M365アカウント侵害時の初動対応
lhazy
7
5.1k
Featured
See All Featured
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
53k
Designing for Performance
lara
610
69k
Build your cross-platform service in a week with App Engine
jlugia
231
18k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
How to train your dragon (web standard)
notwaldorf
96
6.1k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
10
1k
For a Future-Friendly Web
brad_frost
179
9.8k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
667
120k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
Faster Mobile Websites
deanohume
308
31k
RailsConf 2023
tenderlove
30
1.2k
Documentation Writing (for coders)
carmenintech
72
4.9k
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! 🙏