Skip to content

Instantly share code, notes, and snippets.

@fxg42
Last active March 9, 2023 18:44
Show Gist options
  • Save fxg42/570ebca305db1756dab72378071801ba to your computer and use it in GitHub Desktop.
Save fxg42/570ebca305db1756dab72378071801ba to your computer and use it in GitHub Desktop.
Do-notation for the Maybe monad implemented in JavaScript with a generator function
//
// Simplified (and mostly incorrect) definition of the Maybe monad.
//
class Maybe { }
class Nothing extends Maybe { }
class Just extends Maybe {
constructor(v) {
super(v);
this.v = v;
}
}
// a -> m a
const unit = (v) => new Just(v);
// m a -> (a -> m b) -> m b
const bind = (m, f) => m instanceof Nothing ? m : f(m.v);
//
// Curried mathematical operations that return 'something' when the result is
// finite or 'empty' otherwise.
//
// ( a -> b -> c ) -> (a -> b -> m c)
const safe = (f) => (y) => (x) => {
const ret = f(y)(x);
return Number.isFinite(ret) ? unit(ret) : new Nothing();
};
const add = safe((y) => (x) => x + y);
const sub = safe((y) => (x) => x - y);
const mul = safe((y) => (x) => x * y);
const div = safe((y) => (x) => x / y);
//
// Example usage of the 'bind' binary function (>>=). Note that when dividing by
// 0, the chain of operations is short-circuited.
//
bind(unit(1), add(1)) //=> Just { v: 2 }
bind(bind(unit(1), add(1)), mul(3)) //=> Just { v: 6 }
bind(unit(6), div(2)) //=> Just { v: 3 }
bind(unit(6), div(0)) //=> Nothing {}
bind(bind(unit(6), div(0)), mul(3)) //=> Nothing {}
//
// Implementation of a 'do-notation' evaluation context using a generator
// function. Heavily inspired by https://github.com/tj/co/blob/master/index.js
//
const _do = (gen) => {
gen = gen();
const step = (value) => next(gen.next(value));
const next = ({ value, done }) => {
const m = value instanceof Maybe ? value : unit(value);
return done ? m : bind(m, step);
};
return step();
};
//
// Example usage of the 'do-notation' context for the equivalent:
//
// bind(bind(unit(1), add(1)), mul(3)) //=> Just { v: 6 }
//
_do(function* () {
const x = yield 1;
const y = yield add(1)(x);
const z = yield mul(3)(y);
return z;
}); //=> Just { v: 6 }
//
// Another example showing the short circuit that happens when dividing by 0.
//
_do(function* () {
const x = yield 6;
const y = yield div(0)(x);
const z = yield mul(3)(y); // will not get executed.
return z;
}); //=> Nothing {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment