crocks
Version:
A collection of well known Algebraic Datatypes for your utter enjoyment.
393 lines (310 loc) • 9.46 kB
JavaScript
/** @license ISC License (c) copyright 2017 original and current authors */
/** @author Ian Hofmann-Hicks (evil) */
var VERSION = 5
var _implements = require('../core/implements')
var _inspect = require('../core/inspect')
var type = require('../core/types').type('Async')
var _type = require('../core/types').typeFn(type(), VERSION)
var fl = require('../core/flNames')
var array = require('../core/array')
var compose = require('../core/compose')
var once = require('../core/once')
var unit = require('../core/_unit')
var isArray = require('../core/isArray')
var isFoldable = require('../core/isFoldable')
var isFunction = require('../core/isFunction')
var isInteger = require('../core/isInteger')
var isPromise = require('../core/isPromise')
var isSameType = require('../core/isSameType')
var allAsyncs = function (xs) { return xs.reduce(function (acc, x) { return acc && isSameType(Async, x); }, true); }
var _of =
function (x) { return Async(function (_, resolve) { return resolve(x); }); }
var Rejected =
function (x) { return Async(function (reject) { return reject(x); }); }
function all(asyncs) {
if(!(isFoldable(asyncs) && allAsyncs(asyncs))) {
throw new TypeError('Async.all: Foldable structure of Asyncs required')
}
if(isArray(asyncs)) {
return array.sequence(Async.of, asyncs)
}
return asyncs.sequence(Async.of)
}
function fromNode(fn, ctx) {
if(!isFunction(fn)) {
throw new TypeError('Async.fromNode: CPS function required')
}
return function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
return Async(function (reject, resolve) {
fn.apply(ctx,
args.concat(
function (err, data) { return err ? reject(err) : resolve(data); }
)
)
});
}
}
function fromPromise(fn) {
if(!isFunction(fn)) {
throw new TypeError('Async.fromPromise: Promise returning function required')
}
return function() {
var promiseArgs = arguments
return Async(function(reject, resolve) {
var promise = fn.apply(null, promiseArgs)
if(!isPromise(promise)) {
throw new TypeError('Async.fromPromise: Promise returning function required')
}
promise
.then(resolve, reject)
})
}
}
function rejectAfter(ms, value) {
if(!(isInteger(ms) && ms >= 0)) {
throw new TypeError(
'Async.rejectAfter: Positive Integer required for first argument'
)
}
return Async(function (rej) {
var token = setTimeout(function () {
rej(value)
}, ms)
return function () { clearTimeout(token) }
})
}
function resolveAfter(ms, value) {
if(!(isInteger(ms) && ms >= 0)) {
throw new TypeError(
'Async.resolveAfter: Positive Integer required for first argument'
)
}
return Async(function (_, res) {
var token = setTimeout(function () {
res(value)
}, ms)
return function () { clearTimeout(token) }
})
}
function Async(fn) {
var obj;
if(!isFunction(fn)) {
throw new TypeError('Async: Function required')
}
var of =
_of
var inspect =
function () { return ("Async" + (_inspect(fn))); }
function fork(reject, resolve, cleanup) {
if(!isFunction(reject) || !isFunction(resolve)) {
throw new TypeError('Async.fork: Reject and resolve functions required')
}
var cancelled = false
var settled = false
var cancel =
function () { cancelled = true }
var forkCancel =
isFunction(cleanup) ? cleanup : unit
var settle = function (f, x) {
if(!settled) {
settled = true
if(cancelled) {
return unit()
}
return f(x)
}
}
var internal = fn(
settle.bind(null, reject),
settle.bind(null, resolve)
)
var internalFn = isFunction(internal) ? internal : unit
return once(function () { return forkCancel(cancel(internalFn())); })
}
function toPromise() {
return new Promise(function(resolve, reject) {
fork(reject, resolve)
})
}
function race(m) {
if(!isSameType(Async, m)) {
throw new TypeError('Async.race: Async required')
}
return Async(function(reject, resolve) {
var settle = once(
function (resolved, value) { return resolved ? resolve(value) : reject(value); }
)
var res = settle.bind(null, true)
var rej = settle.bind(null, false)
var cancelOne = fork(rej, res)
var cancelTwo = m.fork(rej, res)
return function () { cancelOne(); cancelTwo() }
})
}
function swap(l, r) {
if(!isFunction(l) || !isFunction(r)) {
throw new TypeError('Async.swap: Functions required for both arguments')
}
return Async(function(reject, resolve) {
return fork(
compose(resolve, l),
compose(reject, r)
)
})
}
function coalesce(l, r) {
if(!isFunction(l) || !isFunction(r)) {
throw new TypeError('Async.coalesce: Functions required for both arguments')
}
return Async(function(reject, resolve) {
return fork(
compose(resolve, l),
compose(resolve, r)
)
})
}
function map(method) {
return function(mapFn) {
if(!isFunction(mapFn)) {
throw new TypeError(("Async." + method + ": Function required"))
}
return Async(function(reject, resolve) {
return fork(reject, compose(resolve, mapFn))
})
}
}
function bimap(method) {
return function(l, r) {
if(!isFunction(l) || !isFunction(r)) {
throw new TypeError(("Async." + method + ": Functions required for both arguments"))
}
return Async(function(reject, resolve) {
return fork(
compose(reject, l),
compose(resolve, r)
)
})
}
}
function alt(method) {
return function(m) {
if(!isSameType(Async, m)) {
throw new TypeError(("Async." + method + ": Async required"))
}
return Async(function (rej, res) {
var cancel = unit
var innerCancel = unit
cancel = fork(
function () { innerCancel = m.fork(rej, res) },
res
)
return once(function () { return innerCancel(cancel()); })
})
}
}
function ap(m) {
if(!isSameType(Async, m)) {
throw new TypeError('Async.ap: Async required')
}
return Async(function(reject, resolve) {
var apFn = null
var value = null
var fnDone = false
var valueDone = false
var cancelled = false
var cancel = function () { cancelled = true }
var rejectOnce = once(reject)
function resolveBoth() {
if(!cancelled && fnDone && valueDone) {
compose(resolve, apFn)(value)
}
}
var fnCancel = fork(rejectOnce, function(f) {
if(!isFunction(f)) {
throw new TypeError('Async.ap: Wrapped value must be a function')
}
fnDone = true
apFn = f
resolveBoth()
})
var valueCancel = m.fork(rejectOnce, function (x) {
valueDone = true
value = x
resolveBoth()
})
return function () { fnCancel(); valueCancel(); cancel() }
})
}
function chain(method) {
return function(mapFn) {
if(!isFunction(mapFn)) {
throw new TypeError(
("Async." + method + ": Async returning function required")
)
}
return Async(function(reject, resolve) {
var cancel = unit
var innerCancel = unit
cancel = fork(reject, function(x) {
var m = mapFn(x)
if(!isSameType(Async, m)) {
throw new TypeError(
("Async." + method + ": Function must return another Async")
)
}
innerCancel = m.fork(reject, resolve)
})
return once(function () { return innerCancel(cancel()); })
})
}
}
function bichain(l, r) {
var bichainErr = 'Async.bichain: Both arguments must be Async returning functions'
if(!isFunction(l) || !isFunction(r)) {
throw new TypeError(bichainErr)
}
return Async(function(rej, res) {
var cancel = unit
var innerCancel = unit
function setInnerCancel(mapFn) {
return function(x) {
var m = mapFn(x)
if(!isSameType(Async, m)) {
throw new TypeError(bichainErr)
}
innerCancel = m.fork(rej, res)
}
}
cancel = fork(setInnerCancel(l), setInnerCancel(r))
return once(function () { return innerCancel(cancel()); })
})
}
return ( obj = {
fork: fork, toPromise: toPromise, inspect: inspect,
toString: inspect, type: type,
swap: swap, race: race, coalesce: coalesce, ap: ap,
of: of,
alt: alt('alt'),
bimap: bimap('bimap'),
map: map('map'),
chain: chain('chain'),
bichain: bichain
}, obj[fl.of] = of, obj[fl.alt] = alt(fl.alt), obj[fl.bimap] = bimap(fl.bimap), obj[fl.map] = map(fl.map), obj[fl.chain] = chain(fl.chain), obj['@@type'] = _type, obj.constructor = Async, obj )
}
Async.of = _of
Async.type = type
Async[fl.of] = _of
Async['@@type'] = _type
Async.Rejected = Rejected
Async.Resolved = _of
Async.fromPromise = fromPromise
Async.fromNode = fromNode
Async.all = all
Async.rejectAfter = rejectAfter
Async.resolveAfter = resolveAfter
Async['@@implements'] = _implements(
[ 'alt', 'ap', 'bimap', 'chain', 'map', 'of' ]
)
module.exports = Async