Skip to content

Instantly share code, notes, and snippets.

@VonHeikemen
Created December 26, 2020 19:08
Show Gist options
  • Save VonHeikemen/a70479feeb59a26cd5f217ea752cf115 to your computer and use it in GitHub Desktop.
Save VonHeikemen/a70479feeb59a26cd5f217ea752cf115 to your computer and use it in GitHub Desktop.
Contrived example of transducer in action
// 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