UNPKG

crocks

Version:

A collection of well known Algebraic Datatypes for your utter enjoyment.

353 lines (277 loc) 8.33 kB
/** @license ISC License (c) copyright 2016 original and current authors */ /** @author Ian Hofmann-Hicks (evil) */ var VERSION = 4 var _equals = require('./equals') var _implements = require('./implements') var _inspect = require('./inspect') var type = require('./types').type('List') var _type = require('./types').typeFn(type(), VERSION) var fl = require('./flNames') var array = require('./array') var apOrFunc = require('./apOrFunc') var isApplicative = require('./isApplicative') var isApply = require('./isApply') var isArray = require('./isArray') var isEmpty = require('./isEmpty') var isFunction = require('./isFunction') var isPredOrFunc = require('./isPredOrFunc') var isSameType = require('./isSameType') var isSemigroup = require('./isSemigroup') var predOrFunc = require('./predOrFunc') var not = function (fn) { return function (x) { return !fn(x); }; } var _prepend = function (x) { return function (m) { return x.concat(m); }; } var ref = require('./Maybe'); var Nothing = ref.Nothing; var Just = ref.Just; var _of = function (x) { return List([ x ]); } var _empty = function () { return List([]); } function fromArray(xs) { if(!isArray(xs)) { throw new TypeError('List.fromArray: Array required') } return xs.reduce(function (res, x) { return res.concat(List.of(x)); }, List.empty()) } function applyTraverse(x, y) { if(isArray(x)) { return array.ap(x, array.map(function (v) { return _prepend(List.of(v)); }, y)) } return y .map(function (v) { return _prepend(List.of(v)); }) .ap(x) } function runSequence(acc, x) { if(!((isApply(acc) || isArray(acc)) && isSameType(acc, x))) { throw new TypeError( 'List.sequence: Must wrap Applys of the same type' ) } return applyTraverse(acc, x) } function runTraverse(f) { return function(acc, x) { var m = f(x) if(!((isApply(acc) || isArray(acc)) && isSameType(acc, m))) { throw new TypeError('List.traverse: Both functions must return an Apply of the same type') } return applyTraverse(acc, m) } } function List(x) { var obj; if(!arguments.length) { throw new TypeError('List: List must wrap something') } var xs = isArray(x) ? x.slice() : [ x ] function flatMap(method, fn) { return function(y, x) { var m = fn(x) if(!isSameType(List, m)) { throw new TypeError(("List." + method + ": Function must return a List")) } return y.concat(m.valueOf()) } } var of = _of var valueOf = function () { return xs.slice(); } var toArray = valueOf var empty = _empty var inspect = function () { return ("List" + (_inspect(xs))); } var head = function () { return xs.length ? Just(xs[0]) : Nothing(); } var tail = function () { return xs.length && xs.length > 1 ? Just(List(xs.slice(1))) : Nothing(); } var cons = function (x) { return List([ x ].concat(xs)); } var equals = function (m) { return isSameType(List, m) && _equals(xs, m.valueOf()); } function concat(method) { return function(m) { if(!isSameType(List, m)) { throw new TypeError(("List." + method + ": List required")) } return List(xs.concat(m.valueOf())) } } function reduce(method) { return function(fn, i) { if(!isFunction(fn)) { throw new TypeError(("List." + method + ": Function required for first argument")) } return xs.reduce(fn, i) } } function reduceRight(fn, i) { if(!isFunction(fn)) { throw new TypeError('List.reduceRight: Function required for first argument') } return xs.reduceRight(fn, i) } function fold() { if(isEmpty(xs)) { throw new TypeError('List.fold: List must contain at least one Semigroup') } var head = xs[0] if(!isSemigroup(head)) { throw new TypeError('List.fold: List must contain Semigroups of the same type') } return xs.reduce(function(x, y) { if(!isSameType(x, y)) { throw new TypeError('List.fold: List must contain Semigroups of the same type') } return x.concat(y) }) } function foldMap(fn) { if(!isFunction(fn)) { throw new TypeError( 'List.foldMap: Semigroup returning function required' ) } if(isEmpty(xs)) { throw new TypeError( 'List.foldMap: List must not be empty' ) } var head = fn(xs[0]) if(!isSemigroup(head)) { throw new TypeError( 'List.foldMap: Provided function must return Semigroups of the same type' ) } return xs.length !== 1 ? xs.slice(1).reduce(function(semi, x) { var val = fn(x) if(!(isSameType(semi, val) && isSemigroup(val))) { throw new TypeError( 'List.foldMap: Provided function must return Semigroups of the same type' ) } return semi.concat(val) }, head) : head } function filter(method) { return function(pred) { if(!isPredOrFunc(pred)) { throw new TypeError(("List." + method + ": Pred or predicate function required")) } return List( xs.reduce( function (x, y) { return predOrFunc(pred, y) ? x.concat([ y ]) : x; }, [] ) ) } } function reject(pred) { if(!isPredOrFunc(pred)) { throw new TypeError('List.reject: Pred or predicate function required') } var fn = not(function (x) { return predOrFunc(pred, x); }) return List( xs.reduce( function (x, y) { return fn(y) ? x.concat([ y ]) : x; }, [] ) ) } function map(method) { return function(fn) { if(!isFunction(fn)) { throw new TypeError(("List." + method + ": Function required")) } return List(xs.map(function (x) { return fn(x); })) } } function ap(m) { if(!isSameType(List, m)) { throw new TypeError('List.ap: List required') } var ar = m.valueOf() return List( xs.reduce(function (acc, fn) { if(!isFunction(fn)) { throw new TypeError('List.ap: Wrapped values must all be functions') } return acc.concat(ar.map(function (x) { return fn(x); })) }, []) ) } function chain(method) { return function(fn) { if(!isFunction(fn)) { throw new TypeError(("List." + method + ": Function required")) } return List(xs.reduce(flatMap(method, fn), [])) } } function sequence(f) { if(!(isApplicative(f) || isFunction(f))) { throw new TypeError( 'List.sequence: Applicative TypeRep or Apply returning function required' ) } var af = apOrFunc(f) return reduceRight( runSequence, af(List.empty()) ) } function traverse(f, fn) { if(!(isApplicative(f) || isFunction(f))) { throw new TypeError( 'List.traverse: Applicative TypeRep or Apply returning function required for first argument' ) } if(!isFunction(fn)) { throw new TypeError( 'List.traverse: Apply returning functions required for second argument' ) } var af = apOrFunc(f) return reduceRight( runTraverse(fn), af(List.empty()) ) } return ( obj = { inspect: inspect, toString: inspect, valueOf: valueOf, toArray: toArray, head: head, tail: tail, cons: cons, type: type, equals: equals, empty: empty, reduceRight: reduceRight, fold: fold, foldMap: foldMap, reject: reject, ap: ap, of: of, sequence: sequence, traverse: traverse, concat: concat('concat'), map: map('map'), chain: chain('chain'), reduce: reduce('reduce'), filter: filter('filter') }, obj[fl.of] = of, obj[fl.equals] = equals, obj[fl.concat] = concat(fl.concat), obj[fl.empty] = empty, obj[fl.map] = map(fl.map), obj[fl.chain] = chain(fl.chain), obj[fl.reduce] = reduce(fl.reduce), obj[fl.filter] = filter(fl.filter), obj['@@type'] = _type, obj.constructor = List, obj ) } List.of = _of List.empty = _empty List.type = type List[fl.of] = _of List[fl.empty] = _empty List['@@type'] = _type List.fromArray = fromArray List['@@implements'] = _implements( [ 'ap', 'chain', 'concat', 'empty', 'equals', 'map', 'of', 'reduce', 'traverse' ] ) module.exports = List