Skip to content

Instantly share code, notes, and snippets.

@scott-christopher
Last active December 7, 2015 21:01
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 scott-christopher/4f9ff47fee00feca88d3 to your computer and use it in GitHub Desktop.
Save scott-christopher/4f9ff47fee00feca88d3 to your computer and use it in GitHub Desktop.
Functional JS Generators
"use strict";
const R = require('ramda');
const Maybe = require('ramda-fantasy').Maybe;
const oneTwoThree = Gen(function*() {
yield 1;
yield 2;
yield 3;
});
const fourFiveSix = Gen(function*() {
yield 4;
yield 5;
yield 6;
});
const maybes = Gen(function*() {
yield Maybe.Just(1);
yield Maybe.Just(2);
yield Maybe.Just(3);
});
const plusMinus = x => Gen(function*() { yield x; yield -x; });
const gMap = R.map(R.inc, oneTwoThree);
const gAp = R.lift(R.add)(oneTwoThree, oneTwoThree);
const gChain = R.chain(plusMinus, oneTwoThree);
console.log([...gMap]); // [ 2, 3, 4 ]
console.log([...gAp]); // [ 2, 3, 4, 3, 4, 5, 4, 5, 6 ]
console.log([...gChain]); // [ 1, -1, 2, -2, 3, -3 ]
console.log([...oneTwoThree.concat(fourFiveSix)]); // [ 1, 2, 3, 4, 5, 6 ]
console.log(fourFiveSix.reduce(R.add, 0)); // 15
console.log(maybes.sequence(Maybe.of).map(xs => [...xs])); // Just [ 1, 2, 3 ]
"use strict";
const Gen = gen => ({
concat: gA => Gen(function*() {
yield* gen();
yield* gA;
}),
map: fn => Gen(function*() {
for (let a of gen()) yield fn(a);
}),
ap: gA => Gen(gen).chain(fn => gA.map(fn)),
chain: fn => Gen(function*() {
for (let a of gen()) yield* fn(a);
}),
reduce: (fn, init) => {
var acc = init;
for (let a of gen()) acc = fn(acc, a);
return acc;
},
sequence: of => Gen(gen).reduce(
(acc, x) => acc.map(a => a.concat).ap(x.map(Gen.of)),
of(Gen.empty())
),
[Symbol.iterator]: gen
});
Gen.empty = () => Gen(function*() {});
Gen.of = x => Gen(function*() { yield x; });
"use strict";
const R = require('ramda');
const jsv = require('jsverify');
const GenArb = arb => jsv.array(arb).smap(
xs => Gen(function*() { for (let x of xs) yield x; }),
gen => [...gen]
);
const genInt = GenArb(jsv.integer);
const laws = eq => ({
monad: {
associativity: (mArb, fArb, gArb) => jsv.forall(mArb, fArb, gArb, (m, f, g) => eq(
m.chain(f).chain(g),
m.chain(a => f(a).chain(g))
)),
leftIdentity: (of, aArb, fArb) => jsv.forall(aArb, fArb, (a, f) => eq(
of(a).chain(f),
f(a)
)),
rightIdentity: (of, mArb) => jsv.forall(mArb, m => eq(
m.chain(of),
m
))
}
});
const genLaws = laws((g1, g2) => R.equals([...g1], [...g2]));
jsv.check(genLaws.monad.associativity(genInt, jsv.fn(genInt), jsv.fn(genInt)));
jsv.check(genLaws.monad.leftIdentity(Gen.of, jsv.integer, jsv.fn(genInt)));
jsv.check(genLaws.monad.rightIdentity(Gen.of, genInt));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment