UNPKG

crocks

Version:

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

393 lines (310 loc) 9.46 kB
/** @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