JSFiddle - React, Tailwind, and code Playground

by dp0ch

JavaScript

const MAYBE = Symbol('MAYBE');

const Nothing = Object.freeze({
	bind: () => Nothing,
  toString: () => 'Nothing',
  isSome: false,
  isNone: true,
  get value() {throw new Error('Tried to access value on Nothing')},
  or: (v) => Just(v),
  [MAYBE]: true,
});

function Just (a) {
	if (a && a[MAYBE]) return a;

	const monad = Object.freeze({
  	bind: (f) => Maybe(f(a)),
    toString: () => `Just(${a})`,
    isSome: true,
    isNone: false,
    get value() {return a},
    or: () => monad,
    [MAYBE]: true,
  });

  return monad;
}

function Maybe(v) {
	return (
  	v === undefined ||
    v === null ||
    (typeof v === "number" && isNaN(v))
  ) ? Nothing : Just(v);
}

Maybe.do = (generator) => (...args) => {
	const iterator = generator(...args);

  function iterBind(v) {
  	const {value, done} = iterator.next(v);
    if(done) return Maybe(value);
    return Maybe(value).bind(iterBind);
  }

  return Just().bind(iterBind);
}

Maybe.all = (maybes) => maybes
	.map(Maybe)
	.reduce((acc, maybe) => acc.bind((vals) => maybe.bind((val) => [...vals, val])), Just([]));

function op() {
	return Math.random() < 0.5 ? Nothing : Just(Math.floor(Math.random() * 10));
}

function NaN50() {
	return Math.random() < 0.5 ? NaN : 1;
}

//TEST

const maybeAdd = () => Maybe.all([op(), op(), op()])
  .bind(([a, b, c]) => a + b + c);

const maybeAddDo = Maybe.do(function* () {
	const a = yield op();
  const b = yield op();
  const c = yield op();
  return a + b + c;
});

console.log(maybeAdd().or(null).value);
console.log(maybeAddDo().or(null).value);