// Common Utilities function compose(...fns) { const apply = (arg, fn) => fn(arg); return (initial) => fns.reduceRight(apply, initial); } function curry(arity, fn, ...rest) { if(arity <= rest.length) { return fn(...rest); } return curry.bind(null, arity, fn, ...rest); } // Transducers stuff const Combine = { push: (state, value) => (state.push(value), state), add: (state, value) => state.add(value), concat: (state, value) => state.concat(value) }; function reduce(reducer, initial, collection) { let state = initial; for(let value of collection) { state = reducer(state, value); } return state; } function transduce(combine, initial, transducer, collection) { return reduce(transducer(combine), initial, collection); } const Into = { array: curry(2, function(transducer, collection) { return transduce(Combine.push, [], transducer, collection); }), string: curry(2, function(transducer, collection) { return transduce(Combine.concat, "", transducer, collection) }), set: curry(2, function(transducer, collection) { return transduce(Combine.add, new Set(), transducer, collection); }), }; function filter(predicate, next) { if(arguments.length === 1) { return (_next) => filter(predicate, _next); } return function reducer(state, value) { if(predicate(value)) { return next(state, value); } return state; }; } function map(transform, next) { if(arguments.length === 1) { return (_next) => map(transform, _next); } return function reducer(state, value) { return next(state, transform(value)); }; } // Contrived example const is_even = number => number % 2 === 0; const is_positive = number => number > 0; const add_message = number => `The number is: ${number}`; const transducer = compose( filter(is_positive), filter(is_even), map(add_message) ); const array = [-2, -1, 0, 1, 2, 3]; const set = new Set([-2, -1, 0, 1, 2, 3]); const to_string = Into.string(transducer); console.log( '\nTransducer with Array.reduce\n', array.reduce(transducer(Combine.push), []), '\n' ); console.log( 'Transducer with custom reduce (on Array)\n', Into.array(transducer, array), '\n' ); console.log( 'Transducer with custom reduce (on Set)\n', Into.set(transducer, set), '\n' ); console.log( 'Transducer with custom reduce (from Array to String)\n', Into.string(transducer, array), '\n' ); console.log( 'Transducer with custom reduce (from Set to String)\n', to_string(set), );