Eric Elliott explains _Transducers: Efficient Data Processing Pipelines in JavaScript_ medium
A transducer is a composable higher-order reducer. It takes a reducer as input, and returns another reducer. Transducers are: * Composable using simple function composition * Efficient for large collections or infinite streams: Only enumerates over the elements once, regardless of the number of operations in the pipeline * Able to transduce over any enumerable source (e.g., arrays, trees, streams, graphs, etc…) * Usable for either lazy or eager evaluation with no changes to the transducer pipeline Reducers fold multiple inputs into single outputs.
_(they're a mashup of Iterators and Reducers)_
Some popular libraries which support transducers include Ramda, RxJS, and Mori.
# Reducers
// Sums: (1, 2) = 3 const add = (a, c) => a + c; // Products: (2, 4) = 8 const multiply = (a, c) => a * c; // String concatenation: ('abc', '123') = 'abc123' const concatString = (a, c) => a + c; // Array concatenation: ([1,2], [3,4]) = [1, 2, 3, 4] const concatArray = (a, c) => [...a, ...c];
# Kinda like `map`
const double = x => x * 2; const arr = [1, 2, 3]; const result = arr.map(double);
# Transducers
reducer = (accumulator, current) => accumulator transducer = reducer => reducer
transducer \ = ((accumulator, current) => accumulator) \ => ((accumulator, current) => accumulator)
Most transducers need partial application to specialize them. A map transducer might look like this:
map = transform => reducer => reducer
map = (a => b) => step => reducer
Naive examples:
const compose = (...fns) => x => fns.reduceRight((y, f) => f(y), x); const map = f => step => (a, c) => step(a, f(c)); const filter = predicate => step => (a, c) => predicate(c) ? step(a, c) : a; const isEven = n => n % 2 === 0; const double = n => n * 2; const doubleEvens = compose( filter(isEven), map(double) ); const arrayConcat = (a, c) => a.concat([c]); const xform = doubleEvens(arrayConcat); const result = [1,2,3,4,5,6].reduce(xform, []); // [4, 8, 12] console.log(result);
// import a standard curry, or use this magic spell: const curry = ( f, arr = [] ) => (...args) => ( a => a.length === f.length ? f(...a) : curry(f, a) )([...arr, ...args]); const transduce = curry((step, initial, xform, foldable) => foldable.reduce(xform(step), initial) );