Last active
November 24, 2018 16:29
-
-
Save MHerszak/416be1ec0c58393ac23b5f86fd7fd6f0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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