Skip to content

Instantly share code, notes, and snippets.

@VonHeikemen
Created December 26, 2020 19:07

Revisions

  1. VonHeikemen created this gist Dec 26, 2020.
    118 changes: 118 additions & 0 deletions example-transducers-v1.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,118 @@
    // 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),
    );