UNPKG

monet

Version:

Monadic types library for JavaScript

1,302 lines (1,300 loc) 42.3 kB
/** * Monet.js 0.9.3 * * (c) 2012-2021 Chris Myers * @license Monet.js may be freely distributed under the MIT license. * For all details and documentation: * https://monet.github.io/monet.js/ */ (function(root, factory) { if (typeof define === "function" && define.amd) { define(factory); } else if (typeof module === "object" && module.exports) { module.exports = factory(root); } else { root.Monet = factory(root); } })(typeof self !== "undefined" ? self : this, function() { "use strict"; function assignImp(target, source) { for (var key in source) { if (source.hasOwnProperty(key) && source[key] !== undefined) { target[key] = source[key]; } } return target; } var assign = isFunction(Object.assign) ? Object.assign : assignImp; var Monet = { apply2: apply2, assign: assign, compose: compose, curry: curry(swap(curry), [])([]), idFunction: idFunction, isFunction: isFunction, noop: noop, swap: swap }; var TYPE_KEY = "@@type"; var LIB_NAME = "monet.js"; var TYPES_NAMES = { Identity: "Identity", Maybe: "Maybe", Either: "Either", Validation: "Validation", List: "List", NEL: "NEL", IO: "IO", MonadT: "MonadT", Reader: "Reader", Free: "Free" }; function setType(target, typeName) { target[TYPE_KEY] = LIB_NAME + "/" + typeName; } function isInstance(typeName) { return function(target) { return (target[TYPE_KEY] || target.constructor[TYPE_KEY]) === LIB_NAME + "/" + typeName; }; } function isOfType(typeName) { return function(target) { var targetType = target[TYPE_KEY] || target.constructor && target.constructor[TYPE_KEY]; return Boolean(targetType) && targetType.length >= typeName.length && targetType.indexOf(typeName) === targetType.length - typeName.length; }; } function isNothing(value) { return value == null; } function isEmpty(value) { if (isNothing(value) || value === "") { return true; } if (Array.isArray(value) && value.length === 0) { return true; } if (typeof value === "object") { return Object.keys(value).length === 0; } return false; } function noop() {} function getArgs(args) { return Array.prototype.slice.call(args); } function curry(fn, args) { return function() { var args1 = args.concat(getArgs(arguments)); return args1.length >= fn.length ? fn.apply(null, args1.slice(0, args1.length)) : curry(fn, args1); }; } function compose(f, g) { return function(x) { return f(g(x)); }; } function isFunction(f) { return Boolean(f && f.constructor && f.call && f.apply); } function idFunction(value) { return value; } function trueFunction() { return true; } function areEqual(a, b) { if (a === b || a !== a && b !== b) { return true; } if (!a || !b) { return false; } if (isFunction(a.equals) && isFunction(b.equals)) { return a.equals(b); } return false; } function equals(a) { return function(b) { return areEqual(a, b); }; } function falseFunction() { return false; } function swap(f) { return function(a, b) { return f(b, a); }; } function apply2(a1, a2, f) { return a2.ap(a1.map(curry(f, []))); } function listEquals(list1, list2) { var a = list1; var b = list2; while (!a.isNil && !b.isNil) { if (!equals(a.head())(b.head())) { return false; } a = a.tail(); b = b.tail(); } return a.isNil && b.isNil; } function listMapC(fn, l) { return l.isNil ? Return(l) : Suspend(function() { return listMapC(fn, l.tail()); }).map(curry(cons, [])(fn(l.head()))); } function listMap(fn, l) { return listMapC(fn, l).run(); } function listFilter(list, fn) { return list.foldRight(Nil)(function(a, acc) { return fn(a) ? cons(a, acc) : acc; }); } function listFindC(l, fn) { if (l.isNil) { return Return(None()); } var h = l.head(); return fn(h) ? Return(Some(h)) : Suspend(function() { return listFindC(l.tail(), fn); }); } function listFind(l, fn) { return listFindC(l, fn).run(); } function listContainsC(l, val) { if (l.isNil) { return Return(false); } var h = l.head(); return areEqual(h, val) ? Return(true) : Suspend(function() { return listContainsC(l.tail(), val); }); } function listContains(l, val) { return listContainsC(l, val).run(); } function cons(head, tail) { return tail.cons(head); } function List() { switch (arguments.length) { case 0: return new List.fn.init(); case 1: return new List.fn.init(arguments[0]); default: return new List.fn.init(arguments[0], arguments[1]); } } Monet.List = List; var listForEach = function(effectFn, l) { if (!l.isNil) { effectFn(l.head()); listForEach(effectFn, l.tail()); } }; var foldLeft = function(fn, acc, list) { function fL(innerAcc, innerList) { return innerList.isNil ? Return(innerAcc) : Suspend(function() { return fL(fn(innerAcc, innerList.head()), innerList.tail()); }); } return fL(acc, list).run(); }; var foldRight = function(fn, list, acc) { function fR(innerList, innerAcc) { return innerList.isNil ? Return(innerAcc) : Suspend(function() { return fR(innerList.tail(), innerAcc); }).map(function(accumulated) { return fn(innerList.head(), accumulated); }); } return fR(list, acc).run(); }; var append = function(self, other) { function appendFree(listA, listB) { return listA.isNil ? Return(listB) : Suspend(function() { return appendFree(listA.tail(), listB).map(function(list) { return list.cons(listA.head()); }); }); } return appendFree(self, other).run(); }; var sequence = function(list, type) { return list.foldRight(type.of(Nil))(type.map2(cons)); }; var sequenceValidation = function(list) { return list.foldLeft(Success(Nil))(function(acc, a) { return acc.ap(a.map(function(v) { return function(t) { return cons(v, t); }; })); }).map(listReverse); }; var listReverse = function(list) { return list.foldLeft(Nil)(swap(cons)); }; var listAp = function(list1, list2) { return list1.bind(function(x) { return list2.map(function(f) { return f(x); }); }); }; var Nil; List.fn = List.prototype = { init: function() { var head = arguments[0]; var tail = arguments[1]; if (arguments.length === 0) { this.isNil = true; this.size_ = 0; } else { this.isNil = false; this.head_ = head; this.tail_ = tail || Nil; this.size_ = this.tail_.size() + 1; } setType(this, TYPES_NAMES.List); }, of: function(value) { return new List(value); }, size: function() { return this.size_; }, equals: function(other) { return (List.isOfType(other) || NEL.isOfType(other)) && listEquals(this, other); }, cons: function(head) { return List(head, this); }, snoc: function(element) { return this.concat(List(element)); }, map: function(fn) { return listMap(fn, this); }, toArray: function() { return foldLeft(function(acc, e) { acc.push(e); return acc; }, [], this); }, toSet: function() { return new Set(this); }, foldLeft: function(initialValue) { var self = this; return function(fn) { return foldLeft(fn, initialValue, self); }; }, foldRight: function(initialValue) { var self = this; return function(fn) { return foldRight(fn, self, initialValue); }; }, append: function(list2) { return append(this, list2); }, filter: function(fn) { return listFilter(this, fn); }, find: function(fn) { return listFind(this, fn); }, flatten: function() { return foldRight(append, this, Nil); }, flattenMaybe: function() { return this.flatMap(Maybe.toList); }, reverse: function() { return listReverse(this); }, bind: function(fn) { return this.map(fn).flatten(); }, forEach: function(effectFn) { listForEach(effectFn, this); }, contains: function(val) { return listContains(this, val); }, sequenceMaybe: function() { return sequence(this, Maybe); }, sequenceValidation: function() { return sequenceValidation(this); }, sequenceEither: function() { return sequence(this, Either); }, sequenceIO: function() { return sequence(this, IO); }, sequenceReader: function() { return sequence(this, Reader); }, sequence: function(monadType) { return sequence(this, monadType); }, head: function() { return this.head_; }, headMaybe: function() { return this.isNil ? None() : Some(this.head_); }, lookup: function(i) { return this.isNil || i >= this.size() ? None() : Maybe.fromNull(this.toArray()[i]); }, nth: function(i) { return this.isNil || i >= this.size() ? undefined : this.toArray()[i]; }, tail: function() { return this.isNil ? Nil : this.tail_; }, tails: function() { return this.isNil ? List(Nil, Nil) : this.tail().tails().cons(this); }, ap: function(list) { return listAp(this, list); }, apTo: function(listWithValues) { return listAp(listWithValues, this); }, isNEL: falseFunction, toString: function() { return this.isNil ? "Nil" : "List(" + this.toArray().join(", ") + ")"; }, inspect: function() { return this.toString(); } }; List.fn.init.prototype = List.fn; setType(List, TYPES_NAMES.List); setType(List.fn.init, TYPES_NAMES.List); List.isInstance = isInstance(TYPES_NAMES.List); List.isOfType = isOfType(TYPES_NAMES.List); List.prototype.empty = function() { return Nil; }; List.fromArray = function(array) { return array.reduceRight(function(acc, next) { return acc.cons(next); }, Nil); }; List.from = function(iterable) { return List.fromArray(Array.from(iterable)); }; List.of = function(a) { return new List(a, Nil); }; List.prototype.each = List.prototype.forEach; Nil = Monet.Nil = new List.fn.init(); function emptyNELError(head) { return new Error("Cannot create an empty Non-Empty List. Passed head is " + head + "."); } function NEL(head, tail) { if (isNothing(head)) { throw emptyNELError(head); } return new NEL.fn.init(head, tail); } Monet.NEL = Monet.NonEmptyList = NEL; NEL.of = function(a) { return NEL(a, Nil); }; NEL.fn = NEL.prototype = { init: function(head, tail) { if (isNothing(head)) { throw emptyNELError(head); } else { this.isNil = false; this.head_ = head; this.tail_ = isNothing(tail) ? Nil : tail; this.size_ = this.tail_.size() + 1; } setType(this, TYPES_NAMES.NEL); }, equals: function(other) { return List.isOfType(other) || NEL.isOfType(other) && listEquals(this, other); }, cons: function(head) { return NEL(head, this.toList()); }, snoc: function(element) { return this.concat(NEL(element)); }, map: function(fn) { return NEL(fn(this.head_), listMap(fn, this.tail_)); }, bind: function(fn) { var p = fn(this.head_); if (!p.isNEL()) { throw new Error("NEL.fn.bind: Passed function must return a NonEmptyList."); } var list = this.tail().foldLeft(Nil.snoc(p.head()).append(p.tail()))(function(acc, e) { var list2 = fn(e).toList(); return acc.snoc(list2.head()).append(list2.tail()); }); return new NEL(list.head(), list.tail()); }, head: function() { return this.head_; }, lookup: function(i) { return i >= this.size() ? None() : Maybe.fromNull(this.toArray()[i]); }, nth: function(i) { return i >= this.size() ? undefined : this.toArray()[i]; }, tail: function() { return this.tail_; }, tails: function() { var listsOfNels = this.toList().tails().map(NEL.fromList).flattenMaybe(); return NEL(listsOfNels.head(), listsOfNels.tail()); }, toList: function() { return List(this.head_, this.tail_); }, reverse: function() { if (this.tail().isNil) { return this; } var reversedTail = this.tail().reverse(); return NEL(reversedTail.head(), reversedTail.tail().append(List(this.head()))); }, foldLeft: function(initialValue) { return this.toList().foldLeft(initialValue); }, foldRight: function(initialValue) { return this.toList().foldRight(initialValue); }, reduceLeft: function(fn) { return this.tail().foldLeft(this.head())(fn); }, filter: function(fn) { return listFilter(this.toList(), fn); }, find: function(fn) { return listFind(this.toList(), fn); }, flatten: function() { return foldRight(append, this.toList().map(function(l) { return l.isNEL() ? l.toList() : l; }), Nil); }, flattenMaybe: function() { return this.toList().flatMap(Maybe.toList); }, contains: function(val) { return listContains(this.toList(), val); }, append: function(list2) { return NEL.fromList(this.toList().append(list2.toList())).some(); }, cobind: function(fn) { return this.cojoin().map(fn); }, size: function() { return this.size_; }, forEach: function(fn) { return this.toList().forEach(fn); }, isNEL: trueFunction, toString: function() { return "NEL(" + this.toArray().join(", ") + ")"; }, inspect: function() { return this.toString(); } }; NEL.fromList = function(list) { return list.isNil ? None() : Some(NEL(list.head(), list.tail())); }; NEL.fromArray = function(array) { return NEL.fromList(List.fromArray(array)); }; NEL.from = function(iterable) { return NEL.fromList(List.from(iterable)); }; NEL.fn.init.prototype = NEL.fn; setType(NEL, TYPES_NAMES.NEL); setType(NEL.fn.init, TYPES_NAMES.NEL); NEL.isInstance = isInstance(TYPES_NAMES.NEL); NEL.isOfType = isOfType(TYPES_NAMES.NEL); NEL.prototype.toArray = List.prototype.toArray; NEL.prototype.toSet = List.prototype.toSet; NEL.prototype.extract = NEL.prototype.copure = NEL.prototype.head; NEL.prototype.cojoin = NEL.prototype.tails; NEL.prototype.coflatMap = NEL.prototype.mapTails = NEL.prototype.cobind; NEL.prototype.ap = List.prototype.ap; NEL.prototype.apTo = List.prototype.apTo; var Maybe = Monet.Maybe = {}; Maybe.fromFalsy = function(val) { return !val ? Maybe.None() : Maybe.Some(val); }; Maybe.fromNull = function(val) { return isNothing(val) ? Maybe.None() : Maybe.Some(val); }; Maybe.fromUndefined = function(val) { return val === undefined ? Maybe.None() : Maybe.Some(val); }; Maybe.fromEmpty = function(val) { return isEmpty(val) ? Maybe.None() : Maybe.Some(val); }; Maybe.of = function(a) { return Some(a); }; var Some = Maybe.Just = Maybe.Some = Maybe.some = Monet.Some = Monet.Just = function(val) { return new Maybe.fn.init(true, val); }; var None = Maybe.Nothing = Maybe.None = Maybe.none = Monet.None = Monet.Nothing = function() { return new Maybe.fn.init(false, null); }; Maybe.toList = function(maybe) { return maybe.toList(); }; Maybe.fn = Maybe.prototype = { init: function(isValue, val) { this.isValue = isValue; if (isValue && isNothing(val)) { throw new Error("Can not create Some with illegal value: " + val + "."); } this.val = val; setType(this, TYPES_NAMES.Maybe); }, isSome: function() { return this.isValue; }, isNone: function() { return !this.isSome(); }, bind: function(bindFn) { return this.isValue ? bindFn(this.val) : this; }, some: function() { if (this.isValue) { return this.val; } throw new Error("Cannot call .some() on a None."); }, orSome: function(otherValue) { return this.isValue ? this.val : otherValue; }, orLazy: function(getOtherValue) { return this.cata(getOtherValue, idFunction); }, orNull: function() { return this.orSome(null); }, orUndefined: function() { return this.orSome(undefined); }, orElse: function(maybe) { return this.catchMap(function() { return maybe; }); }, ap: function(maybeWithFunction) { var value = this.val; return this.isValue ? maybeWithFunction.map(function(fn) { return fn(value); }) : this; }, apTo: function(maybeWithValue) { return maybeWithValue.ap(this); }, equals: function(other) { return Maybe.isOfType(other) && this.cata(function() { return other.isNone(); }, function(val) { return other.fold(false)(equals(val)); }); }, toArray: function() { return this.map(function(val) { return [ val ]; }).orLazy(function() { return []; }); }, toSet: function() { return new Set(this); }, toList: function() { return this.map(List).orLazy(function() { return Nil; }); }, toEither: function(failVal) { return this.isSome() ? Right(this.val) : Left(failVal); }, toValidation: function(failVal) { return this.isSome() ? Success(this.val) : Fail(failVal); }, fold: function(defaultValue) { var self = this; return function(fn) { return self.isSome() ? fn(self.val) : defaultValue; }; }, foldLeft: function(initialValue) { return this.toList().foldLeft(initialValue); }, foldRight: function(initialValue) { return this.toList().foldRight(initialValue); }, cata: function(none, some) { return this.isSome() ? some(this.val) : none(); }, catchMap: function(fn) { return this.isSome() ? this : fn(); }, filter: function(fn) { var self = this; return self.flatMap(function(a) { return fn(a) ? self : None(); }); }, orNoneIf: function(bool) { return bool ? None() : this; }, contains: function(val) { return this.isSome() ? areEqual(this.val, val) : false; }, forEach: function(fn) { this.cata(noop, fn); }, orElseRun: function(fn) { this.cata(fn, noop); }, toString: function() { return this.isSome() ? "Just(" + this.val + ")" : "Nothing"; }, inspect: function() { return this.toString(); } }; Maybe.prototype.orJust = Maybe.prototype.getOrElse = Maybe.prototype.orSome; Maybe.prototype.just = Maybe.prototype.some; Maybe.prototype.isJust = Maybe.prototype.isSome; Maybe.prototype.isNothing = Maybe.prototype.isNone; Maybe.prototype.orNothingIf = Maybe.prototype.orNoneIf; Maybe.fn.init.prototype = Maybe.fn; setType(Maybe, TYPES_NAMES.Maybe); setType(Maybe.fn.init, TYPES_NAMES.Maybe); Maybe.isInstance = isInstance(TYPES_NAMES.Maybe); Maybe.isOfType = isOfType(TYPES_NAMES.Maybe); var Validation = Monet.Validation = {}; var Success = Validation.Success = Validation.success = Monet.Success = function(val) { return new Validation.fn.init(val, true); }; var Fail = Validation.Fail = Validation.fail = Monet.Fail = function(error) { return new Validation.fn.init(error, false); }; Validation.of = function(v) { return Success(v); }; Validation.fn = Validation.prototype = { init: function(val, success) { this.val = val; this.isSuccessValue = success; setType(this, TYPES_NAMES.Validation); }, success: function() { if (this.isSuccess()) { return this.val; } throw new Error("Cannot call success() on a Fail."); }, isSuccess: function() { return this.isSuccessValue; }, isFail: function() { return !this.isSuccessValue; }, fail: function() { if (this.isSuccess()) { throw new Error("Cannot call fail() on a Success."); } return this.val; }, bind: function(fn) { return this.isSuccess() ? fn(this.val) : this; }, ap: function(validationWithFn) { var value = this.val; return this.isSuccess() ? validationWithFn.map(function(fn) { return fn(value); }) : validationWithFn.isFail() ? Validation.Fail(Semigroup.append(value, validationWithFn.fail())) : this; }, apTo: function(validationWithValue) { return validationWithValue.ap(this); }, acc: function() { var x = function() { return x; }; return this.isSuccessValue ? Validation.success(x) : this; }, foldLeft: function(initialValue) { return this.toMaybe().toList().foldLeft(initialValue); }, foldRight: function(initialValue) { return this.toMaybe().toList().foldRight(initialValue); }, cata: function(fail, success) { return this.isSuccessValue ? success(this.val) : fail(this.val); }, catchMap: function(fn) { return this.isSuccess() ? this : fn(this.val); }, swap: function() { return this.isSuccess() ? Fail(this.val) : Success(this.val); }, failMap: function(fn) { return this.isFail() ? Fail(fn(this.val)) : this; }, bimap: function(fail, success) { return this.isSuccessValue ? this.map(success) : this.failMap(fail); }, forEach: function(fn) { this.cata(noop, fn); }, forEachFail: function(fn) { this.cata(fn, noop); }, equals: function(other) { return Validation.isOfType(other) && this.cata(function(fail) { return other.cata(equals(fail), falseFunction); }, function(success) { return other.cata(falseFunction, equals(success)); }); }, toMaybe: function() { return this.isSuccess() ? Some(this.val) : None(); }, toEither: function() { return (this.isSuccess() ? Right : Left)(this.val); }, toString: function() { return (this.isSuccess() ? "Success(" : "Fail(") + this.val + ")"; }, inspect: function() { return this.toString(); } }; Validation.prototype.fold = Validation.prototype.cata; Validation.fn.init.prototype = Validation.fn; setType(Validation, TYPES_NAMES.Validation); setType(Validation.fn.init, TYPES_NAMES.Validation); Validation.isInstance = isInstance(TYPES_NAMES.Validation); Validation.isOfType = isOfType(TYPES_NAMES.Validation); var Semigroup = Monet.Semigroup = { append: function(a, b) { if (isFunction(a.concat)) { return a.concat(b); } throw new Error("Couldn't find a semigroup appender in the environment, " + "please specify your own append function"); } }; var MonadT = Monet.monadTransformer = Monet.MonadT = Monet.monadT = function(monad) { return new MonadT.fn.init(monad); }; MonadT.of = function(m) { return MonadT(m); }; MonadT.fn = MonadT.prototype = { init: function(monad) { this.monad = monad; setType(Validation, TYPES_NAMES.MonadT); }, map: function(fn) { return MonadT(this.monad.map(function(v) { return v.map(fn); })); }, bind: function(fn) { return MonadT(this.monad.map(function(v) { return v.flatMap(fn); })); }, ap: function(monadWithFn) { return MonadT(this.monad.flatMap(function(v) { return monadWithFn.perform().map(function(v2) { return v.ap(v2); }); })); }, perform: function() { return this.monad; } }; MonadT.fn.init.prototype = MonadT.fn; var IO = Monet.IO = Monet.io = function(effectFn) { return new IO.fn.init(effectFn); }; IO.of = function(a) { return IO(function() { return a; }); }; IO.fn = IO.prototype = { init: function(effectFn) { if (!isFunction(effectFn)) { throw new Error("IO requires a function."); } this.effectFn = effectFn; setType(this, TYPES_NAMES.IO); }, map: function(fn) { var self = this; return IO(function() { return fn(self.effectFn()); }); }, bind: function(fn) { var self = this; return IO(function() { return fn(self.effectFn()).run(); }); }, ap: function(ioWithFn) { var self = this; return ioWithFn.map(function(fn) { return fn(self.effectFn()); }); }, apTo: function(ioWithValue) { return ioWithValue.ap(this); }, run: function() { return this.effectFn(); } }; IO.fn.init.prototype = IO.fn; setType(IO, TYPES_NAMES.IO); setType(IO.fn.init, TYPES_NAMES.IO); IO.isInstance = isInstance(TYPES_NAMES.IO); IO.isOfType = isOfType(TYPES_NAMES.IO); IO.prototype.perform = IO.prototype.performUnsafeIO = IO.prototype.run; var Either = Monet.Either = {}; Either.of = function(a) { return Right(a); }; Either.fromTry = function(fn) { try { return Either.right(fn()); } catch (e) { return Either.left(e); } }; Either.fromPromise = function(promise) { return promise.then(Either.Right, Either.Left); }; var Right = Either.Right = Either.right = Monet.Right = function(val) { return new Either.fn.init(val, true); }; var Left = Either.Left = Either.left = Monet.Left = function(val) { return new Either.fn.init(val, false); }; Either.fn = Either.prototype = { init: function(val, isRightValue) { this.isRightValue = isRightValue; this.value = val; setType(this, TYPES_NAMES.Either); }, bind: function(fn) { return this.isRightValue ? fn(this.value) : this; }, ap: function(eitherWithFn) { var self = this; return this.isRightValue ? eitherWithFn.map(function(fn) { return fn(self.value); }) : this; }, apTo: function(eitherWithValue) { return eitherWithValue.ap(this); }, leftMap: function(fn) { return this.isLeft() ? Left(fn(this.value)) : this; }, isRight: function() { return this.isRightValue; }, isLeft: function() { return !this.isRight(); }, right: function() { if (this.isRightValue) { return this.value; } throw new Error("Cannot call right() on a Left."); }, left: function() { if (this.isRightValue) { throw new Error("Cannot call left() on a Right."); } return this.value; }, foldLeft: function(initialValue) { return this.toMaybe().toList().foldLeft(initialValue); }, foldRight: function(initialValue) { return this.toMaybe().toList().foldRight(initialValue); }, cata: function(leftFn, rightFn) { return this.isRightValue ? rightFn(this.value) : leftFn(this.value); }, catchMap: function(fn) { return this.isRight() ? this : fn(this.value); }, swap: function() { return this.isRight() ? Left(this.value) : Right(this.value); }, forEach: function(fn) { this.cata(noop, fn); }, forEachLeft: function(fn) { this.cata(fn, noop); }, equals: function(other) { return Either.isOfType(other) && this.cata(function(left) { return other.cata(equals(left), falseFunction); }, function(right) { return other.cata(falseFunction, equals(right)); }); }, bimap: function(leftFn, rightFn) { return this.isRightValue ? this.map(rightFn) : this.leftMap(leftFn); }, toMaybe: function() { return this.isRight() ? Some(this.value) : None(); }, toValidation: function() { return this.isRight() ? Success(this.value) : Fail(this.value); }, toString: function() { return this.cata(function(left) { return "Left(" + left + ")"; }, function(right) { return "Right(" + right + ")"; }); }, toPromise: function() { return this.cata(function(left) { return Promise.reject(left); }, function(right) { return Promise.resolve(right); }); }, inspect: function() { return this.toString(); } }; Either.prototype.fold = Either.prototype.cata; Either.fn.init.prototype = Either.fn; setType(Either, TYPES_NAMES.Either); setType(Either.fn.init, TYPES_NAMES.Either); Either.isInstance = isInstance(TYPES_NAMES.Either); Either.isOfType = isOfType(TYPES_NAMES.Either); var Reader = Monet.Reader = function(fn) { return new Reader.fn.init(fn); }; Reader.of = function(x) { return Reader(function(_) { return x; }); }; Reader.ask = function() { return Reader(idFunction); }; Reader.fn = Reader.prototype = { init: function(fn) { this.f = fn; setType(this, TYPES_NAMES.Reader); }, run: function(config) { return this.f(config); }, bind: function(fn) { var self = this; return Reader(function(config) { return fn(self.run(config)).run(config); }); }, ap: function(readerWithFn) { var self = this; return readerWithFn.bind(function(fn) { return Reader(function(config) { return fn(self.run(config)); }); }); }, apTo: function(readerWithValue) { return readerWithValue.ap(this); }, map: function(fn) { var self = this; return Reader(function(config) { return fn(self.run(config)); }); }, local: function(fn) { var self = this; return Reader(function(c) { return self.run(fn(c)); }); } }; Reader.fn.init.prototype = Reader.fn; setType(Reader, TYPES_NAMES.Reader); setType(Reader.fn.init, TYPES_NAMES.Reader); Reader.isInstance = isInstance(TYPES_NAMES.Reader); Reader.isOfType = isOfType(TYPES_NAMES.Reader); var Free = Monet.Free = {}; var Suspend = Free.Suspend = Monet.Suspend = function(functor) { return new Free.fn.init(functor, true); }; var Return = Free.Return = Monet.Return = function(val) { return new Free.fn.init(val, false); }; Free.of = function(a) { return Return(a); }; Free.liftF = function(functor) { return isFunction(functor) ? Suspend(compose(Return, functor)) : Suspend(functor.map(Return)); }; Free.fn = Free.prototype = { init: function(val, isSuspend) { this.isSuspend = isSuspend; if (isSuspend) { this.functor = val; } else { this.val = val; } setType(this, TYPES_NAMES.Free); }, run: function() { return this.go(function(f) { return f(); }); }, bind: function(fn) { return this.isSuspend ? isFunction(this.functor) ? Suspend(compose(function(free) { return free.bind(fn); }, this.functor)) : Suspend(this.functor.map(function(free) { return free.bind(fn); })) : fn(this.val); }, ap: function(ff) { return this.bind(function(x) { return ff.map(function(f) { return f(x); }); }); }, apTo: function(f) { return f.ap(this); }, resume: function() { return this.isSuspend ? Left(this.functor) : Right(this.val); }, go1: function(f) { function go2(t) { return t.resume().cata(function(functor) { return go2(f(functor)); }, idFunction); } return go2(this); }, go: function(f) { var result = this.resume(); while (result.isLeft()) { var next = f(result.left()); result = next.resume(); } return result.right(); } }; Free.fn.init.prototype = Free.fn; setType(Free, TYPES_NAMES.Free); setType(Free.fn.init, TYPES_NAMES.Free); Free.isInstance = isInstance(TYPES_NAMES.Free); Free.isOfType = isOfType(TYPES_NAMES.Free); function Identity(a) { return new Identity.fn.init(a); } Monet.Identity = Identity; Identity.of = function(a) { return new Identity(a); }; Identity.fn = Identity.prototype = { init: function(val) { this.val = val; setType(this, TYPES_NAMES.Identity); }, bind: function(fn) { return fn(this.val); }, get: function() { return this.val; }, forEach: function(fn) { fn(this.val); }, equals: function(other) { return Identity.isOfType(other) && equals(this.get())(other.get()); }, contains: function(val) { return areEqual(this.val, val); }, toString: function() { return "Identity(" + this.val + ")"; }, inspect: function() { return this.toString(); }, ap: function(applyWithFunction) { var value = this.val; return applyWithFunction.map(function(fn) { return fn(value); }); }, apTo: function(identityWithValue) { return identityWithValue.ap(this); }, toArray: function() { return [ this.get() ]; }, toList: function() { return List(this.get(), Nil); }, toSet: function() { return new Set(this); } }; Identity.fn.init.prototype = Identity.fn; setType(Identity, TYPES_NAMES.Identity); setType(Identity.fn.init, TYPES_NAMES.Identity); Identity.isInstance = isInstance(TYPES_NAMES.Identity); Identity.isOfType = isOfType(TYPES_NAMES.Identity); function addFantasyLandAliases(type) { [ "equals", "map", "ap", "chain" ].filter(function(method) { return isFunction(type.prototype[method]); }).forEach(function(method) { type.prototype["fantasy-land/" + method] = type.prototype[method]; }); } function addAliases(type) { type.prototype.flatMap = type.prototype.chain = type.prototype.bind; type.pure = type.unit = type.of; type.prototype.of = type.of; if (isFunction(type.prototype.append)) { type.prototype.concat = type.prototype.append; } type.prototype.point = type.prototype.pure = type.prototype.unit = type.prototype.of; } function addFilterNot(type) { if (isFunction(type.prototype.filter)) { type.prototype.filterNot = function(fn) { return this.filter(function(a) { return !fn(a); }); }; } } function addMonadOps(type) { type.prototype.join = function() { return this.flatMap(idFunction); }; type.map2 = function(fn) { return function(ma, mb) { return ma.flatMap(function(a) { return mb.map(function(b) { return fn(a, b); }); }); }; }; } function addFunctorOps(type) { if (!isFunction(type.prototype.map)) { type.prototype.map = function(fn) { return this.bind(compose(this.of, fn)); }; } } function addApplicativeOps(type) { type.prototype.takeLeft = function(m) { return apply2(this, m, function(a, b) { return a; }); }; type.prototype.takeRight = function(m) { return apply2(this, m, function(a, b) { return b; }); }; } function addCollectionPredicates(type) { if (isFunction(type.prototype.toArray)) { type.prototype.every = type.prototype.forall = function(fn) { return this.toArray().every(fn); }; type.prototype.exists = function(fn) { return this.toArray().some(fn); }; } } function makeIterable(type) { if (isFunction(type.prototype.toArray)) { type.prototype[Symbol.iterator] = function() { return this.toArray()[Symbol.iterator](); }; } } function addToOperator(type) { if (isFunction(type.prototype.toArray)) { type.prototype.to = function(ctor) { return ctor(this); }; } } function decorate(type) { addAliases(type); addFilterNot(type); addMonadOps(type); addFunctorOps(type); addApplicativeOps(type); addCollectionPredicates(type); addFantasyLandAliases(type); makeIterable(type); addToOperator(type); } decorate(MonadT); decorate(Either); decorate(Maybe); decorate(IO); decorate(NEL); decorate(List); decorate(Validation); decorate(Reader); decorate(Free); decorate(Identity); return Monet; });