Last active
March 9, 2023 18:44
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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