crocks
Version:
A collection of well known Algebraic Datatypes for your utter enjoyment.
353 lines (277 loc) • 8.33 kB
JavaScript
/** @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