Eric Elliott had an interesting tweet that intrigued me: https://twitter.com/_ericelliott/status/912407669683609600?s=03
Here's what he posted:
const map = transform => reducer => ((acc, current) => reducer(acc, transform(current)))
const filter = predicate => reducer => (
(acc, current) => predicate(current) ? reducer(acc, current) : acc
)
const isEven = filter(x => x % 2 === 0)
const double = map(n => n * 2)
In practice, we can apply this technique with something like this:
[1,2,3,4,5,6,7,8,9,10].reduce(double(
(acc, current) => {
// console.log('acc', acc);
// console.log('current', current);
acc.push(current);
return acc;
}
), [])
//[4, 8, 12, 16, 20]
But at first glance, I couldn't understand what was going on so I did some research and got some help from this article https://medium.com/@roman01la/understanding-transducers-in-javascript-3500d3bd9624
To help me "teach" myself, I deconstructed map
and filter
. When we deconstruct, we get something like this:
var mapSquaredReduce = (acc, current, index, ary) => {
return (
//think of this as an IIFE. And the IIFE is getting arguments from the main (acc, current, index, ary)
//and the IIFE is outputting/returning "acc2" which becomes the "acc" which is passed into the next "loop"
(
//this is the returned reducer
(acc2, current2) => {
console.log('acc2', acc2)
console.log('current2', current2)
acc2.push(current2)
return acc2
}
)
(
//these are the arguments passed to the returned reducer (acc2, current2)
acc,
(n => n * 2)(current)
)
)
}
Here's the filter transducer deconstructed
var filterEvenReduce = (acc, current, index, ary) => {
if ((x => x % 2 === 0)(current)) {
return (
(
//this is the returned reducer
(acc2, current2) => {
console.log('acc2', acc2)
console.log('current2', current2)
acc2.push(current2)
return acc2
}
)
(
acc,
current
)
)
} else {
return acc
}
}