Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Boxed: bringing algebraic types to TypeScript
Search
Matthias Le Brun
March 27, 2024
Technology
220
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
140
(why the hell did I) build a GraphQL client for the browser
bloodyowl
0
170
leveraging (algebraic data) types to make your UI rock @ jsheroes
bloodyowl
0
360
Leveraging (algebraic data) types to make your UI rock solid
bloodyowl
0
530
La drôle d'histoire de JavaScript
bloodyowl
0
430
Healthy Code Collaboration
bloodyowl
0
390
Simplify your UI management with (algebraic data) types
bloodyowl
0
880
Simplify your UI management with (algebraic data) types
bloodyowl
1
600
Migrating a large Reason+React codebase to hooks
bloodyowl
0
640
Other Decks in Technology
See All in Technology
スプシの手作業からの解脱 ~更新はClaude Codeに、見せ方と使い方はGASアプリに任せる~
kentarofujii
0
180
局所最適は悪ではない。壊れているのは指標です。量と質を同時に見る先行指標の設計
recruitengineers
PRO
2
270
データエンジニアの次の戦場は "プロセスエンジニアリング"
sansantech
PRO
1
150
小規模OSSモデルは サイバーセキュリティ業務に どこまで使えるか
bbrbbq
0
170
[JAWS-UG彩の国埼玉#12]そろそろ準備する?re:Invent2026をやさしく解説 / reinvent2026-sainokuni
sh_fk2
1
390
トークン化預金DCJPYの取り組みと活用ユースケース
finengine
0
220
AIをリアル世界に解き放つ 〜M5Stack×AIであなたのアイディアを手の平に〜
hamadakoji
1
290
Active Record Connection Adaptersにおける長年の課題に再び取り組む
yahonda
0
410
CloudWatch Logsのコスト最適化:どこまでAIに任せてどこから人間が承認すべきか
yama3133
0
160
野蛮なアジャイル [サイボウズ発!]組織の常識を覆した反骨のスクラム実践記 / Barbaric Agile - Enterprise Agile Study Group
ama_ch
0
120
Steerable Visual Representations
sansantech
PRO
0
120
ガチで放置できるClaude Code実行環境の作り方.pdf
lamaglama39
0
4.4k
Featured
See All Featured
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
1
280
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
3.5k
Why Our Code Smells
bkeepers
PRO
340
58k
Marketing to machines
jonoalderson
1
5.9k
A designer walks into a library…
pauljervisheath
211
25k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
Ethics towards AI in product and experience design
skipperchong
2
440
Faster Mobile Websites
deanohume
310
32k
My Coaching Mixtape
mlcsv
0
360
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
500
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
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! 🙏