Created
December 26, 2020 19:08
-
-
Save VonHeikemen/a70479feeb59a26cd5f217ea752cf115 to your computer and use it in GitHub Desktop.
Contrived example of transducer in action
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
// 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(transducer, initial, collection) { | |
transducer = to_transducer(transducer); | |
if(arguments.length === 2) { | |
collection = initial; | |
initial = transducer['@@transducer/init'](); | |
} | |
let state = initial; | |
for(let value of collection) { | |
state = transducer['@@transducer/step'](state, value); | |
if(state != null && state['@@transducer/reduced']) { | |
state = state['@@transducer/value']; | |
break; | |
} | |
} | |
return transducer['@@transducer/result'](state); | |
} | |
function transduce(combine, initial, transducer, collection) { | |
return reduce( | |
transducer(to_transducer(combine)), | |
initial, | |
collection | |
); | |
} | |
function to_transducer(reducer) { | |
if(typeof reducer['@@transducer/step'] == 'function') { | |
return reducer; | |
} | |
return { | |
'@@transducer/init': function() { | |
throw new Error('Method not implemented'); | |
}, | |
'@@transducer/result': function(state) { | |
return state; | |
}, | |
'@@transducer/step': function(state, value) { | |
return reducer(state, value); | |
} | |
}; | |
} | |
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 { | |
'@@transducer/init': function() { | |
return next['@@transducer/init'](); | |
}, | |
'@@transducer/result': function(state) { | |
return next['@@transducer/result'](state); | |
}, | |
'@@transducer/step': function(state, value) { | |
if(predicate(value)) { | |
return next['@@transducer/step'](state, value); | |
} | |
return state; | |
}, | |
}; | |
} | |
function map(transform, next) { | |
if(arguments.length === 1) { | |
return (_next) => map(transform, _next); | |
} | |
return { | |
'@@transducer/init': function() { | |
return next['@@transducer/init'](); | |
}, | |
'@@transducer/result': function(state) { | |
return next['@@transducer/result'](state); | |
}, | |
'@@transducer/step': function(state, value) { | |
return next['@@transducer/step'](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 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), | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment