Skip to content

Instantly share code, notes, and snippets.

@MHerszak
Last active November 24, 2018 16:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MHerszak/416be1ec0c58393ac23b5f86fd7fd6f0 to your computer and use it in GitHub Desktop.
Save MHerszak/416be1ec0c58393ac23b5f86fd7fd6f0 to your computer and use it in GitHub Desktop.
// Transduceres belong to the family of Structural design patterns compostion
// This GIST covers all of the cases that we would currently use
// .map, .filter, and .reduce for with arrays, and the
// composable transducers don’t make multiple copies of the data set.
// Transducers as developed for production code bases cover more use cases,
// such as replicating the functionality of .find.
// http://raganwald.com/2017/04/30/transducers.html
const arrayOf = (acc, val) => { acc.push(val); return acc; };
const sumOf = (acc, val) => acc + val;
const setOf = (acc, val) => acc.add(val);
const map =
fn =>
reducer =>
(acc, val) => reducer(acc, fn(val));
const filter =
fn =>
reducer =>
(acc, val) =>
fn(val) ? reducer(acc, val) : acc;
const compose = (...fns) =>
fns.reduce((acc, val) => (...args) => val(acc(...args)), x => x);
const transduce = (transformer, reducer, seed, iterable) => {
const transformedReducer = transformer(reducer);
let accumulation = seed;
for (const value of iterable) {
accumulation = transformedReducer(accumulation, value);
}
return accumulation;
}
// OR https://medium.com/javascript-scene/transducers-efficient-data-processing-pipelines-in-javascript-7985330fe73d
// import a standard curry, or use this:
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)
);
// Test
const concatArray = (a, c) => a.concat([c]);
const toArray = transduce(concatArray, []);
const isEven = n => n % 2 === 0;
const double = n => n * 2;
const doubleEvens = compose(
filter(isEven),
map(double)
);
// Manual transduce:
const xform = doubleEvens(arrayConcat);
const result = [1,2,3,4,5,6].reduce(xform, []);
// => [4, 8, 12]
// Automatic transduce:
const result2 = toArray(doubleEvens, [1,2,3,4,5,6]);
console.log(result2); // [4, 8, 12]
/**
Transducer Rules
Initialization: Given no initial accumulator value, a transducer must call the step
function to produce a valid initial value to act on. The value should represent the empty state.
For example, an accumulator that accumulates an array should supply an empty array when its
step function is called with no arguments.
Early termination: A process that uses transducers must check for and stop when it receives
a reduced accumulator value. Additionally, a transducer step function that uses a nested reduce
must check for and convey reduced values when they are encountered.
Completion (optional): Some transducing processes never complete, but those that do should call
the completion function to produce a final value and/or flush state, and stateful transducers
should supply a completion operation that cleans up any accumulated resources and potentially
produces one final value.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment