Skip to content

Instantly share code, notes, and snippets.

@lilactown
Created August 1, 2017 16:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lilactown/bfcbad9736d2ed1783d735d44b330fae to your computer and use it in GitHub Desktop.
Save lilactown/bfcbad9736d2ed1783d735d44b330fae to your computer and use it in GitHub Desktop.
type Reduced<Value> = { value: Value, completed: boolean };
type Reducer<Accumulator, Input> = (whatever: Accumulator, input: Input) => Accumulator | Reduced<Accumulator>;
type Transducer<Input, Output> =
<Accumulator>(reducer: Reducer<Accumulator, Output>) => Reducer<Accumulator, Input>;
// utils
function reduced<Value>(value: any, completed: boolean): Reduced<Value> {
return { value, completed };
}
function isReduced<Value>(v: any): v is Reduced<Value> {
return v && v.value && v.completed !== undefined;
}
// transducers
function map<Input, Output>(f: (x: Input) => Output): Transducer<Input, Output> {
return function mapTransducer<Accumulator>(reducer: Reducer<Accumulator, Output>): Reducer<Accumulator, Input> {
return function mapReducer(whatever, input) {
return reducer(whatever, f(input));
}
}
}
function filter<Input>(p: (x: Input) => boolean): Transducer<Input, Input> {
return function filterTransducer<Accumulator>(reducer: Reducer<Accumulator, Input>): Reducer<Accumulator, Input> {
return function filterReducer(whatever, input) {
return p(input) ? reducer(whatever, input) : whatever;
}
}
}
// BROKEN
function concat<Accumulator, Input>(reducer: Reducer<Accumulator, Input>): Reducer<Accumulator, Input> {
return function concatReducer(whatever, input) {
return reducer(whatever, input);
}
}
// function comp<T extends Transducer<any, any>>(...xfs: T[]): T
function compose(...xfs: Transducer<any, any>[]): Transducer<any, any> {
return function compTransducer(reducer) {
return xfs.reverse().reduce((xform, xf) => xf(xform), reducer);
}
}
function mapCat<Input, Output>(f: (x: Input) => Output[]): Transducer<Input, Output> {
return compose(map(f), concat);
}
function take<Value>(n: number) {
return function takeTransducer<Accumulator>(reducer: Reducer<Accumulator, Value>): Reducer<Accumulator, Value> {
let count = 0;
return function takeReducer(whatever, input) {
if (count < n) {
count++;
return reducer(whatever, input);
}
return reduced(whatever, true);
}
}
}
// do transduction
function transduce<Input, Output, T extends Transducer<Input, any>>(
xf: T,
f: Reducer<Output, any>,
init: Output,
coll: Iterable<Input>
) {
const reducer = xf(f);
let accumulator = init;
for (const el of coll) {
const reducedVal = reducer(accumulator, el);
// console.log(el, reducedVal);
if (isReduced<Output>(reducedVal)) {
if (reducedVal.completed) {
return reducedVal.value;
}
}
else {
accumulator = reducedVal;
}
}
return accumulator;
}
function toArray<Input, Output>(xf: Transducer<Input, Output>, coll: Iterable<Input>): Output[] {
return transduce(xf, (acc, x) => acc.concat([x]), new Array<Output>(), coll);
}
function toFn<Input, Output, Accumulator>(
xf: Transducer<Input, Output>,
f: Reducer<Accumulator, Output>,
// showReduced: boolean = false
): Reducer<Accumulator, Input> {
const reducer = xf(f);
return (acc, x) => {
const val = reducer(acc, x);
if (isReduced<Accumulator>(val)) {
return val.value;
}
return val;
};
}
function* toIter<Input, Output>(xf: Transducer<Input, Output>, coll: Iterable<Input>): IterableIterator<Output> {
const reducer = xf((_acc: Output, x) => x);
/**
* Transforms like `filter` return the accumulator value
*/
const DUMMY_VAL= <Output>{};
const iter = coll[Symbol.iterator]();
let currentEl = iter.next();
while (!currentEl.done) {
let value = reducer(DUMMY_VAL, currentEl.value);
if (isReduced<Output>(value)) {
if (value.completed) {
return value.value;
}
}
else {
yield value;
}
currentEl = iter.next();
}
}
// examples
const inc = map((x: number) => x + 1);
const isEven = filter((x: number) => x % 2 === 0);
const incEven = compose(inc, isEven);
const result = transduce(
compose(inc, take(5)),
(acc, x) => acc + x,
0,
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
);
console.log(result);
const resultArr = toArray(incEven, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
console.log(resultArr);
const incArray = toFn(
inc,
(acc: any[], x) => acc.concat([x])
);
const resultFn = [0,1,2,3,4,5,6,7,8,9].reduce(incArray, []);
console.log(resultFn);
const resultIter = toIter(inc, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
for (const el of resultIter) {
console.log(el);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment