Skip to content

Instantly share code, notes, and snippets.

@scott-christopher
Last active August 29, 2015 14:17
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scott-christopher/5069a91b924e7e182caa to your computer and use it in GitHub Desktop.
Save scott-christopher/5069a91b924e7e182caa to your computer and use it in GitHub Desktop.
Fantasy-Land monads and ES6 generators
module.exports = (genFunc) => (...args) => {
const generator = genFunc(...args);
const cont = arg => {
const {done, value} = generator.next(arg);
return done ? value : value.chain(cont);
};
return cont();
}
const
genDo = require('./gen-do'),
Option = require('fantasy-options'),
phonebook = { 'schristo': '+61491570156' },
govDb = { '+61491570156': 'abc123' },
taxDb = { 'abc123': 'def456' },
lookup = (k, obj) =>
(k in obj) ? Option.Some(obj[k]) : Option.None,
taxCodeLookup = genDo(function*(name) {
const number = yield lookup(name, phonebook);
const reg = yield lookup(number, govDb);
return lookup(reg, taxDb);
});
console.log('Searching for "schristo": ' + taxCodeLookup('schristo').getOrElse('Not found'));
console.log('Searching for "_schristo": ' + taxCodeLookup('_schristo').getOrElse('Not found'));
const
genDo = require('./gen-do'),
Promise = require('fantasy-promises'),
delay = (n, v) =>
new Promise(resolve => setTimeout(() => resolve(v), n)),
afterDelay = genDo(function*(v1, v2) {
console.log('waiting for v1');
const _v1 = yield delay(500, v1);
console.log('got: ' + _v1);
console.log('waiting for v2');
const _v2 = yield delay(500, v2);
console.log('got: ' + _v2);
return Promise.of(_v1 + " " + v2);
});
afterDelay("Hello", "World!").fork(str => console.log(str));
const
genDo = require('./gen-do'),
StateT = require('fantasy-states').StateT,
IO = require('fantasy-io'),
fs = require('fs'),
M = StateT(IO),
prependTo = (a) => (b) => b + a,
readFile = (f) => IO(() => fs.readFileSync(f, 'utf8')),
println = (s) => IO(() => console.log(s)),
program = genDo(function*(f1, f2) {
const f1Str = yield M.lift(readFile(f1));
yield M.modify(prependTo(f1Str));
const f2Str = yield M.lift(readFile(f2));
yield M.modify(prependTo(f2Str));
const state = yield M.get;
return M.lift(println(state));
});
program('gen-do.js', 'state-io-example.js').exec('').unsafePerform();
const
genDo = require('./gen-do'),
StateT = require('fantasy-states').StateT,
Promise = require('fantasy-promises'),
M = StateT(Promise),
question = () =>
Promise.of('What is the answer to Life, the Universe and Everything?'),
deepThought = () => new Promise(function(resolve) {
const sevenAndAHalfMillionYears = 7500000;
setTimeout(resolve, sevenAndAHalfMillionYears / 10000, 42);
}),
prependTo = (a) => (b) => b + ' ' + a,
prog = genDo(function*() {
const q1 = yield M.lift(question());
yield M.modify(prependTo(q1));
const q2 = yield M.lift(deepThought());
yield M.modify(prependTo(q2));
return M.get;
});
prog().exec('>').fork(x => console.log(x));
@scott-christopher
Copy link
Author

The function exported from gen-do.js accepts a generator that yields monads implementing the Fantasy-Land Monad Spec.

This allows for composing monads in an imperative-like form, similar to Haskell's "do" notation, such as the following Option Monad example from option-example.js:

var taxCodeLookup = genDo(function*(name) {
  var number = yield lookup(name, phonebook);
  var reg    = yield lookup(number, govDb);
  return lookup(reg, taxDb);
});

taxCodeLookup('schristo').getOrElse('Not found');

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment