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
Transducers
Search
Amitay Horwitz
December 24, 2023
Programming
0
54
Transducers
Amitay Horwitz
December 24, 2023
Tweet
Share
More Decks by Amitay Horwitz
See All by Amitay Horwitz
Building event sourced systems with Kafka Streams
amitayh
1
1.1k
Event Sourcing with Kafka Streams
amitayh
1
1.2k
Datomic Spotlight
amitayh
0
100
TDD For The Curious
amitayh
0
290
Other Decks in Programming
See All in Programming
そのAIレビュー、レビューしてますか? / Are you reviewing those AI reviews?
rkaga
6
4.4k
AgentCoreとHuman in the Loop
har1101
5
210
AIで開発はどれくらい加速したのか?AIエージェントによるコード生成を、現場の評価と研究開発の評価の両面からdeep diveしてみる
daisuketakeda
1
940
メルカリのリーダビリティチームが取り組む、AI時代のスケーラブルな品質文化
cloverrose
2
510
QAフローを最適化し、品質水準を満たしながらリリースまでの期間を最短化する #RSGT2026
shibayu36
2
4.2k
AIによるイベントストーミング図からのコード生成 / AI-powered code generation from Event Storming diagrams
nrslib
2
1.7k
humanlayerのブログから学ぶ、良いCLAUDE.mdの書き方
tsukamoto1783
0
170
dchart: charts from deck markup
ajstarks
3
980
CSC307 Lecture 05
javiergs
PRO
0
490
GISエンジニアから見たLINKSデータ
nokonoko1203
0
200
フロントエンド開発の勘所 -複数事業を経験して見えた判断軸の違い-
heimusu
7
2.7k
疑似コードによるプロンプト記述、どのくらい正確に実行される?
kokuyouwind
0
360
Featured
See All Featured
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.4k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
170
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
3.3k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
230
My Coaching Mixtape
mlcsv
0
44
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
61
52k
Balancing Empowerment & Direction
lara
5
860
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
1
46
Between Models and Reality
mayunak
1
180
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
190
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
270
Transcript
TRANSDUCERS @amitayh
COLLECTION TRANSFORMATIONS We use them all the time…
const double = x => x * 2; const isEven
= x => x % 2 === 0; const coll = [1, 2, 3, 4, 5, 6];
const double = x => x * 2; const isEven
= x => x % 2 === 0; const coll = [1, 2, 3, 4, 5, 6]; const doubled = coll.map(double); // [2, 4, 6, 8, 10, 12]
const double = x => x * 2; const isEven
= x => x % 2 === 0; const coll = [1, 2, 3, 4, 5, 6]; const doubled = coll.map(double); const even = coll.filter(isEven); // [2, 4, 6]
const double = x => x * 2; const isEven
= x => x % 2 === 0; const coll = [1, 2, 3, 4, 5, 6]; const doubled = coll.map(double); const even = coll.filter(isEven); const sum = coll.reduce( (acc, item) => acc + item, 0 ); // 21
Q: What all these have in common? A: We can
de fi ne all of them in terms of reduce ⁉
const map = (coll, f) => { return coll.reduce( (acc,
item) => [...acc, f(item)], [] ); }; const filter = (coll, pred) => { return coll.reduce( (acc, item) => pred(item) ? [...acc, item] : acc, [] ); };
const map = (coll, f) => { return coll.reduce( (acc,
item) => [...acc, f(item)], [] ); }; const filter = (coll, pred) => { return coll.reduce( (acc, item) => pred(item) ? [...acc, item] : acc, [] ); };
const map = (coll, f) => { return coll.reduce( (acc,
item) => [...acc, f(item)], [] ); }; const filter = (coll, pred) => { return coll.reduce( (acc, item) => pred(item) ? [...acc, item] : acc, [] ); };
const map = (coll, f) => { return coll.reduce( (acc,
item) => [...acc, f(item)], [] ); }; const filter = (coll, pred) => { return coll.reduce( (acc, item) => pred(item) ? [...acc, item] : acc, [] ); };
const map = (coll, f) => { return coll.reduce( (acc,
item) => [...acc, f(item)], [] ); }; const filter = (coll, pred) => { return coll.reduce( (acc, item) => pred(item) ? [...acc, item] : acc, [] ); };
const map = (coll, f) => { return coll.reduce( (acc,
item) => [...acc, f(item)], [] ); }; const filter = (coll, pred) => { return coll.reduce( (acc, item) => pred(item) ? [...acc, item] : acc, [] ); };
OBSERVATIONS 👀 1. The actual logic is in the reducing
function - the rest is boilerplate 2. We are coupled to our input and output types 3. Chaining several operations will introduce intermediate results - wasteful: coll.map(double).filter(isEven)
1⃣
• Problem: logic in the reducing function • Solution: extract
it const map = f => (acc, item) => [...acc, f(item)]; const filter = pred => (acc, item) => pred(item) ? [...acc, item] : acc; const run = (coll, reducer) => coll.reduce(reducer, []);
const coll = [1, 2, 3, 4, 5, 6]; run(coll,
map(double)); // [2, 4, 6, 8, 10, 12] run(coll, filter(isEven)); // [2, 4, 6]
2⃣
• Problem: coupling to input and output • Solution: inject
the “step” function const map = f => (acc, item) => [...acc, f(item)]; const filter = pred => (acc, item) => pred(item) ? [...acc, item] : acc;
• Problem: coupling to input and output • Solution: inject
the “step” function const map = f => (acc, item) => [...acc, f(item)]; const filter = pred => (acc, item) => pred(item) ? [...acc, item] : acc;
• Problem: coupling to input and output • Solution: inject
the “step” function const map = f => step => (acc, item) => [...acc, f(item)]; const filter = pred => step => (acc, item) => pred(item) ? [...acc, item] : acc;
• Problem: coupling to input and output • Solution: inject
the “step” function const map = f => step => (acc, item) => step(acc, f(item)); const filter = pred => step => (acc, item) => pred(item) ? step(acc, item) : acc;
• Problem: coupling to input and output • Solution: inject
the “step” function const map = f => step => (acc, item) => step(acc, f(item)); const filter = pred => step => (acc, item) => pred(item) ? step(acc, item) : acc;
ENTER: TRANSDUCERS // reducer signature: (whatever, input) => whatever //
transducer signature: reducer => reducer
const transduce = (xf, step, acc, input) => { const
reducer = xf(step); for (let item of input) { acc = reducer(acc, item); } return acc; };
const transduce = (xf, step, acc, input) => { const
reducer = xf(step); for (let item of input) { acc = reducer(acc, item); } return acc; }; // Step functions const into = (acc, item) => [...acc, item]; const sum = (acc, item) => acc + item;
// Some transducer const xf = filter(isEven); const coll =
[1, 2, 3, 4, 5, 6]; transduce(xf, into, [], coll); // [2, 4, 6] transduce(xf, sum, 0, coll); // 12
FULL DECOUPLING 😎 • The process is separate from the
input / output sources • Reuse transformation logic • Built in collections (arrays, objects) • Custom collections (Immutable.js) • WebSockets / In fi nite streams
3⃣
• Problem: intermediate results • Solution: function composition! const identity
= x => x; const compose = (...fns) => fns.reduce( (acc, fn) => x => acc(fn(x)), identity );
// Composing transducers const xf = compose( filter(isEven), map(double) );
const coll = [1, 2, 3, 4, 5, 6]; // No intermediate results! transduce(xf, into, [], coll); // [4, 8, 12] transduce(xf, sum, 0, coll); // 24
STATEFUL TRANSDUCERS • Example: drop - remove fi rst n
elements const drop = n => step => { let remaining = n; return (acc, item) => (remaining-- > 0) ? acc : step(acc, item); }; coll = [1, 2, 3, 4, 5, 6]; transduce(drop(4), into, [], coll); // [5, 6]
OTHER COOL TRANSDUCERS • dropWhile(pred) • partition(pred) • dedupe
EARLY TERMINATION const done = x => ({value: x, __done__:
true});
EARLY TERMINATION const done = x => ({value: x, __done__:
true}); const transduce = (xf, step, acc, input) => { const reducer = xf(step); for (let item of input) { acc = reducer(acc, item); } return acc; };
EARLY TERMINATION const done = x => ({value: x, __done__:
true}); const transduce = (xf, step, acc, input) => { const reducer = xf(step); for (let item of input) { acc = reducer(acc, item); if (acc.__done__) { acc = acc.value; break; } } return acc; };
EARLY TERMINATION const done = x => ({value: x, __done__:
true}); const transduce = (xf, step, acc, input) => { const reducer = xf(step); for (let item of input) { acc = reducer(acc, item); if (acc.__done__) { acc = acc.value; break; } } return acc; };
EARLY TERMINATION • Example: take - keep fi rst n
elements const take = n => step => { let remaining = n; return (acc, item) => (remaining-- > 0) ? step(acc, item) : done(acc); }; transduce(take(2), into, [], coll); // [1, 2]
EARLY TERMINATION • Bonus! we can now use in fi
nite collections function* numbers() { let index = 0; while (true) { yield index++; } }
EARLY TERMINATION • Bonus! we can now use in fi
nite collections const xf = compose( filter(isEven), map(double), take(5) ); transduce(xf, into, [], numbers()); // [0, 4, 8, 12, 16]
Q&A 🤓
RESOURCES 📚 • Blog post: http://wix.to/G8DRABw • Talk by Rich
Hickey: http://wix.to/XMDRABw • Transducers in Scala: http://wix.to/XsDRABw • …And in JavaScript: מםכאמם