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)
Slide 15
Slide 15 text
1⃣
Slide 16
Slide 16 text
• 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, []);
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
Slide 29
Slide 29 text
3⃣
Slide 30
Slide 30 text
• Problem: intermediate results
• Solution: function composition!
const identity = x => x;
const compose = (...fns) => fns.reduce(
(acc, fn) => x => acc(fn(x)),
identity
);
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]
Slide 33
Slide 33 text
OTHER COOL TRANSDUCERS
• dropWhile(pred)
• partition(pred)
• dedupe
Slide 34
Slide 34 text
EARLY TERMINATION
const done = x => ({value: x, __done__: true});
Slide 35
Slide 35 text
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;
};
Slide 36
Slide 36 text
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;
};
Slide 37
Slide 37 text
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;
};
Slide 38
Slide 38 text
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]
Slide 39
Slide 39 text
EARLY TERMINATION
• Bonus! we can now use in
fi
nite collections
function* numbers() {
let index = 0;
while (true) {
yield index++;
}
}
Slide 40
Slide 40 text
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]
Slide 41
Slide 41 text
Q&A 🤓
Slide 42
Slide 42 text
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: מםכאמם