UNPKG

fluture

Version:

FantasyLand compliant (monadic) alternative to Promises

1,655 lines (1,385 loc) 69.3 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('concurrify'), require('sanctuary-type-identifiers'), require('sanctuary-show')) : typeof define === 'function' && define.amd ? define(['concurrify', 'sanctuary-type-identifiers', 'sanctuary-show'], factory) : (global = global || self, global.Fluture = factory(global.concurrify, global.sanctuaryTypeIdentifiers, global.sanctuaryShow)); }(this, function (concurrify, type, show) { 'use strict'; concurrify = concurrify && concurrify.hasOwnProperty('default') ? concurrify['default'] : concurrify; type = type && type.hasOwnProperty('default') ? type['default'] : type; show = show && show.hasOwnProperty('default') ? show['default'] : show; /* istanbul ignore next: non v8 compatibility */ var setImmediate = typeof setImmediate === 'undefined' ? setImmediateFallback : setImmediate; function noop(){} function moop(){ return this } function padf(sf, s){ return s.replace(/^/gm, sf).replace(sf, '') } function showf(f){ return padf(' ', show(f)) } function partial1(f, a){ return function bound1(b, c, d){ switch(arguments.length){ case 1: return f(a, b); case 2: return f(a, b, c); default: return f(a, b, c, d); } }; } function partial2(f, a, b){ return function bound2(c, d){ return arguments.length === 1 ? f(a, b, c) : f(a, b, c, d); }; } function partial3(f, a, b, c){ return function bound3(d){ return f(a, b, c, d); }; } function setImmediateFallback(f, x){ return setTimeout(f, 0, x); } function raise(x){ setImmediate(function rethrowErrorDelayedToEscapePromiseCatch(){ throw x; }); } var FL = { alt: 'fantasy-land/alt', ap: 'fantasy-land/ap', bimap: 'fantasy-land/bimap', chain: 'fantasy-land/chain', chainRec: 'fantasy-land/chainRec', map: 'fantasy-land/map', of: 'fantasy-land/of', zero: 'fantasy-land/zero' }; var ordinal = ['first', 'second', 'third', 'fourth', 'fifth']; var namespace = 'fluture'; var name = 'Future'; var version = 4; var $$type = namespace + '/' + name + '@' + version; var nil = {head: null}; nil.tail = nil; function isNil(list){ return list.tail === list; } // cons :: (a, List a) -> List a // -- O(1) append operation function cons(head, tail){ return {head: head, tail: tail}; } // reverse :: List a -> List a // -- O(n) list reversal function reverse(xs){ var ys = nil, tail = xs; while(!isNil(tail)){ ys = cons(tail.head, ys); tail = tail.tail; } return ys; } // cat :: (List a, List a) -> List a // -- O(n) list concatenation function cat(xs, ys){ var zs = ys, tail = reverse(xs); while(!isNil(tail)){ zs = cons(tail.head, zs); tail = tail.tail; } return zs; } /* istanbul ignore next: non v8 compatibility */ var captureStackTrace = Error.captureStackTrace || captureStackTraceFallback; var _debug = noop; function debugMode(debug){ _debug = debug ? debugHandleAll : noop; } function debugHandleAll(fn){ return fn(); } function debug(fn){ return _debug(fn); } function captureContext(previous, tag, fn){ return debug(function debugCaptureContext(){ var context = { tag: tag, name: ' from ' + tag + ':', }; captureStackTrace(context, fn); return cat(previous, cons(context, nil)); }) || previous; } function captureStackTraceFallback(x){ var e = new Error; /* istanbul ignore else: non v8 compatibility */ if(typeof e.stack === 'string'){ x.stack = x.name + '\n' + e.stack.split('\n').slice(1).join('\n'); }else{ x.stack = x.name; } } function error(message){ return new Error(message); } function typeError(message){ return new TypeError(message); } function invalidArgument(it, at, expected, actual){ return typeError( it + '() expects its ' + ordinal[at] + ' argument to ' + expected + '.' + '\n Actual: ' + show(actual) + ' :: ' + type.parse(type(actual)).name ); } function invalidContext(it, actual){ return typeError( it + '() was invoked outside the context of a Future. You might want to use' + ' a dispatcher instead\n Called on: ' + show(actual) ); } function invalidNamespace(m, x){ return ( 'The Future was not created by ' + namespace + '. ' + 'Make sure you transform other Futures to ' + namespace + ' Futures. ' + 'Got ' + (x ? ('a Future from ' + x) : 'an unscoped Future') + '.' + '\n See: https://github.com/fluture-js/Fluture#casting-futures' ); } function invalidVersion(m, x){ return ( 'The Future was created by ' + (x < version ? 'an older' : 'a newer') + ' version of ' + namespace + '. ' + 'This means that one of the sources which creates Futures is outdated. ' + 'Update this source, or transform its created Futures to be compatible.' + '\n See: https://github.com/fluture-js/Fluture#casting-futures' ); } function invalidFuture(it, at, m, s){ var id = type.parse(type(m)); var info = id.name === name ? '\n' + ( id.namespace !== namespace ? invalidNamespace(m, id.namespace) : id.version !== version ? invalidVersion(m, id.version) : 'Nothing seems wrong. Contact the Fluture maintainers.') : ''; return typeError( it + '() expects ' + (ordinal[at] ? 'its ' + ordinal[at] + ' argument to be a valid Future' : at) + '.' + info + '\n Actual: ' + show(m) + ' :: ' + id.name + (s || '') ); } function ensureError(value, fn){ var message; try{ if(value instanceof Error) return value; message = 'A Non-Error was thrown from a Future: ' + show(value); }catch (_){ message = 'Something was thrown from a Future, but it could not be converted to String'; } var e = error(message); captureStackTrace(e, fn); return e; } function makeError(caught, callingFuture, extraContext){ var origin = ensureError(caught, makeError); var e = error(origin.message); e.context = cat(origin.context || nil, extraContext || nil); e.future = origin.future || callingFuture; e.reason = origin.reason || origin; e.stack = e.reason.stack + (isNil(e.context) ? '' : '\n' + contextToStackTrace(e.context)); return e; } function contextToStackTrace(context){ var stack = '', tail = context; while(tail !== nil){ stack += tail.head.stack + '\n'; tail = tail.tail; } return stack; } function throwInvalidArgument(it, at, expected, actual){ throw invalidArgument(it, at, expected, actual); } function throwInvalidContext(it, actual){ throw invalidContext(it, actual); } function throwInvalidFuture(it, at, m, s){ throw invalidFuture(it, at, m, s); } function isFunction(f){ return typeof f === 'function'; } function isThenable(m){ return m instanceof Promise || m != null && isFunction(m.then); } function isBoolean(f){ return typeof f === 'boolean'; } function isNumber(f){ return typeof f === 'number'; } function isUnsigned(n){ return (n === Infinity || isNumber(n) && n > 0 && n % 1 === 0); } function isObject(o){ return o !== null && typeof o === 'object'; } function isIterator(i){ return isObject(i) && isFunction(i.next); } function isArray(x){ return Array.isArray(x); } function hasMethod(method, x){ return x != null && isFunction(x[method]); } function isFunctor(x){ return hasMethod(FL.map, x); } function isAlt(x){ return isFunctor(x) && hasMethod(FL.alt, x); } function isApply(x){ return isFunctor(x) && hasMethod(FL.ap, x); } function isBifunctor(x){ return isFunctor(x) && hasMethod(FL.bimap, x); } function isChain(x){ return isApply(x) && hasMethod(FL.chain, x); } /*eslint no-cond-assign:0, no-constant-condition:0 */ function Future(computation){ if(!isFunction(computation)) throwInvalidArgument('Future', 0, 'be a Function', computation); return new Computation(computation); } function isFuture(x){ return x instanceof Future || type(x) === $$type; } Future['@@type'] = $$type; Future.prototype['@@show'] = function Future$show(){ return this.toString(); }; Future.prototype[FL.ap] = function Future$FL$ap(other){ return other._ap(this); }; Future.prototype[FL.map] = function Future$FL$map(mapper){ return this._map(mapper); }; Future.prototype[FL.bimap] = function Future$FL$bimap(lmapper, rmapper){ return this._bimap(lmapper, rmapper); }; Future.prototype[FL.chain] = function Future$FL$chain(mapper){ return this._chain(mapper); }; Future.prototype[FL.alt] = function Future$FL$alt(other){ return this._alt(other); }; Future.prototype.pipe = function Future$pipe(f){ if(!isFuture(this)) throwInvalidContext('Future#pipe', this); if(!isFunction(f)) throwInvalidArgument('Future#pipe', 0, 'be a Function', f); return f(this); }; Future.prototype.fork = function Future$fork(rej, res){ if(!isFuture(this)) throwInvalidContext('Future#fork', this); if(!isFunction(rej)) throwInvalidArgument('Future#fork', 0, 'be a Function', rej); if(!isFunction(res)) throwInvalidArgument('Future#fork', 1, 'be a Function', res); return this._interpret(raise, rej, res); }; Future.prototype.forkCatch = function Future$forkCatch(rec, rej, res){ if(!isFuture(this)) throwInvalidContext('Future#forkCatch', this); if(!isFunction(rec)) throwInvalidArgument('Future#forkCatch', 0, 'be a Function', rec); if(!isFunction(rej)) throwInvalidArgument('Future#forkCatch', 1, 'be a Function', rej); if(!isFunction(res)) throwInvalidArgument('Future#forkCatch', 2, 'be a Function', res); return this._interpret(rec, rej, res); }; Future.prototype.value = function Future$value(res){ if(!isFuture(this)) throwInvalidContext('Future#value', this); if(!isFunction(res)) throwInvalidArgument('Future#value', 0, 'be a Function', res); var _this = this; return _this._interpret(raise, function Future$value$rej(x){ raise(error( 'Future#value was called on a rejected Future\n' + ' Rejection: ' + show(x) + '\n' + ' Future: ' + _this.toString() )); }, res); }; Future.prototype.done = function Future$done(callback){ if(!isFuture(this)) throwInvalidContext('Future#done', this); if(!isFunction(callback)) throwInvalidArgument('Future#done', 0, 'be a Function', callback); return this._interpret(raise, function Future$done$rej(x){ callback(x); }, function Future$done$res(x){ callback(null, x); }); }; Future.prototype.promise = function Future$promise(){ if(!isFuture(this)) throwInvalidContext('Future#promise', this); var _this = this; return new Promise(function Future$promise$computation(res, rej){ _this._interpret(raise, rej, res); }); }; Future.prototype.extractLeft = function Future$extractLeft(){ return []; }; Future.prototype.extractRight = function Future$extractRight(){ return []; }; Future.prototype._transform = function Future$transform(action){ return new Transformation(this, cons(action, nil)); }; Future.prototype.context = nil; function Computation(computation){ this._computation = computation; this.context = captureContext(nil, 'a Future created with the Future constructor', Future); } Computation.prototype = Object.create(Future.prototype); Computation.prototype._interpret = function Computation$interpret(rec, rej, res){ var open = false, cancel = noop, cont = function(){ open = true; }; var context = captureContext(this.context, 'consuming a Future', Computation$interpret); try{ cancel = this._computation(function Computation$rej(x){ cont = function Computation$rej$cont(){ open = false; rej(x); }; if(open){ cont(); } }, function Computation$res(x){ cont = function Computation$res$cont(){ open = false; res(x); }; if(open){ cont(); } }) || noop; }catch(e){ rec(makeError(e, this, context)); return noop; } if(!(isFunction(cancel) && cancel.length === 0)){ rec(makeError(typeError( 'The computation was expected to return a nullary function or void\n' + ' Actual: ' + show(cancel) ), this, context)); return noop; } cont(); return function Computation$cancel(){ if(open){ open = false; cancel && cancel(); } }; }; Computation.prototype.toString = function Computation$toString(){ return 'Future(' + showf(this._computation) + ')'; }; function Transformation(spawn, actions){ this._spawn = spawn; this._actions = actions; } Transformation.prototype = Object.create(Future.prototype); Transformation.prototype._transform = function Transformation$_transform(action){ return new Transformation(this._spawn, cons(action, this._actions)); }; Transformation.prototype._interpret = function Transformation$interpret(rec, rej, res){ //These are the cold, and hot, action stacks. The cold actions are those that //have yet to run parallel computations, and hot are those that have. var cold = nil, hot = nil; //A linked list of stack traces, tracking context across ticks. var context = captureContext(nil, 'consuming a transformed Future', Transformation$interpret); //The context of the last action to run. var asyncContext = nil; //These combined variables define our current state. // future = the future we are currently forking // action = the action to be informed when the future settles // cancel = the cancel function of the current future // settled = a boolean indicating whether a new tick should start // async = a boolean indicating whether we are awaiting a result asynchronously var future, action, cancel = noop, settled, async = true, it; //Takes an action from the top of the hot stack and returns it. function nextHot(){ var x = hot.head; hot = hot.tail; return x; } //Takes an action from the top of the cold stack and returns it. function nextCold(){ var x = cold.head; cold = cold.tail; return x; } //This function is called with a future to use in the next tick. //Here we "flatten" the actions of another Sequence into our own actions, //this is the magic that allows for infinitely stack safe recursion because //actions like ChainAction will return a new Sequence. //If we settled asynchronously, we call drain() directly to run the next tick. function settle(m){ settled = true; future = m; if(future._spawn){ var tail = future._actions; while(!isNil(tail)){ cold = cons(tail.head, cold); tail = tail.tail; } future = future._spawn; } if(async) drain(); } //This function serves as a rejection handler for our current future. //It will tell the current action that the future rejected, and it will //settle the current tick with the action's answer to that. function rejected(x){ if(async) context = cat(future.context, cat(asyncContext, context)); settle(action.rejected(x)); } //This function serves as a resolution handler for our current future. //It will tell the current action that the future resolved, and it will //settle the current tick with the action's answer to that. function resolved(x){ if(async) context = cat(future.context, cat(asyncContext, context)); settle(action.resolved(x)); } //This function is passed into actions when they are "warmed up". //If the action decides that it has its result, without the need to await //anything else, then it can call this function to force "early termination". //When early termination occurs, all actions which were stacked prior to the //terminator will be skipped. If they were already hot, they will also be //sent a cancel signal so they can cancel their own concurrent computations, //as their results are no longer needed. function early(m, terminator){ context = cat(terminator.context, context); cancel(); cold = nil; if(async && action !== terminator){ action.cancel(); while((it = nextHot()) && it !== terminator) it.cancel(); } settle(m); } //This will cancel the current Future, the current action, and all stacked hot actions. function Sequence$cancel(){ cancel(); action && action.cancel(); while(it = nextHot()) it.cancel(); } //This function is called when an exception is caught. function exception(e){ Sequence$cancel(); settled = true; cold = hot = nil; var error = makeError(e, future, context); future = never; rec(error); } //This function serves to kickstart concurrent computations. //Takes all actions from the cold stack in reverse order, and calls run() on //each of them, passing them the "early" function. If any of them settles (by //calling early()), we abort. After warming up all actions in the cold queue, //we warm up the current action as well. function warmupActions(){ cold = reverse(cold); while(cold !== nil){ it = cold.head.run(early); if(settled) return; hot = cons(it, hot); cold = cold.tail; } action = action.run(early); } //This function represents our main execution loop. By "tick", we've been //referring to the execution of one iteration in the while-loop below. function drain(){ async = false; while(true){ settled = false; if(action) asyncContext = action.context; if(action = nextCold()){ cancel = future._interpret(exception, rejected, resolved); if(!settled) warmupActions(); }else if(action = nextHot()){ cancel = future._interpret(exception, rejected, resolved); }else break; if(settled) continue; async = true; return; } cancel = future._interpret(exception, rej, res); } //Start the execution loop. settle(this); //Return the cancellation function. return Sequence$cancel; }; Transformation.prototype.toString = function Transformation$toString(){ var str = '', tail = this._actions; while(!isNil(tail)){ str = '.' + tail.head.toString() + str; tail = tail.tail; } return this._spawn.toString() + str; }; function Crashed(exception){ this._exception = exception; } Crashed.prototype = Object.create(Future.prototype); Crashed.prototype._interpret = function Crashed$interpret(rec){ rec(this._exception); return noop; }; Crashed.prototype.toString = function Crashed$toString(){ return 'Future(function crash(){ throw ' + show(this._exception) + ' })'; }; function Rejected(value){ this._value = value; } Rejected.prototype = Object.create(Future.prototype); Rejected.prototype._interpret = function Rejected$interpret(rec, rej){ rej(this._value); return noop; }; Rejected.prototype.extractLeft = function Rejected$extractLeft(){ return [this._value]; }; Rejected.prototype.toString = function Rejected$toString(){ return 'reject(' + show(this._value) + ')'; }; function reject(x){ return new Rejected(x); } function Resolved(value){ this._value = value; } Resolved.prototype = Object.create(Future.prototype); Resolved.prototype._interpret = function Resolved$interpret(rec, rej, res){ res(this._value); return noop; }; Resolved.prototype.extractRight = function Resolved$extractRight(){ return [this._value]; }; Resolved.prototype.toString = function Resolved$toString(){ return 'Future.of(' + show(this._value) + ')'; }; function resolve(x){ return new Resolved(x); } function Never(){ this._isNever = true; } Never.prototype = Object.create(Future.prototype); Never.prototype._interpret = function Never$interpret(){ return noop; }; Never.prototype.toString = function Never$toString(){ return 'never'; }; var never = new Never(); function isNever(x){ return isFuture(x) && x._isNever === true; } function Eager(future){ var _this = this; _this.rec = noop; _this.rej = noop; _this.res = noop; _this.crashed = false; _this.rejected = false; _this.resolved = false; _this.value = null; _this.cancel = future._interpret(function Eager$crash(x){ _this.value = x; _this.crashed = true; _this.cancel = noop; _this.rec(x); }, function Eager$reject(x){ _this.value = x; _this.rejected = true; _this.cancel = noop; _this.rej(x); }, function Eager$resolve(x){ _this.value = x; _this.resolved = true; _this.cancel = noop; _this.res(x); }); } Eager.prototype = Object.create(Future.prototype); Eager.prototype._interpret = function Eager$interpret(rec, rej, res){ if(this.crashed) rec(this.value); else if(this.rejected) rej(this.value); else if(this.resolved) res(this.value); else{ this.rec = rec; this.rej = rej; this.res = res; } return this.cancel; }; var Action = { rejected: function Action$rejected(x){ this.cancel(); return new Rejected(x) }, resolved: function Action$resolved(x){ this.cancel(); return new Resolved(x) }, run: moop, cancel: noop }; function captureActionContext(name, fn){ return captureContext(nil, 'a Future transformed with ' + name, fn); } function nullaryActionToString(){ return this.name + '()'; } function defineNullaryAction(name, prototype){ var _name = '_' + name; function NullaryAction(context){ this.context = context; } NullaryAction.prototype = Object.assign(Object.create(Action), prototype); NullaryAction.prototype.name = name; NullaryAction.prototype.toString = nullaryActionToString; Future.prototype[name] = function checkedNullaryTransformation(){ if(!isFuture(this)) throwInvalidContext('Future#' + name, this); return this[_name](); }; Future.prototype[_name] = function uncheckedNullaryTransformation(){ return this._transform(new NullaryAction( captureActionContext(name, uncheckedNullaryTransformation) )); }; return NullaryAction; } function mapperActionToString(){ return this.name + '(' + showf(this.mapper) + ')'; } function defineMapperAction(name, prototype){ var _name = '_' + name; function MapperAction(mapper, context){ this.mapper = mapper; this.context = context; } MapperAction.prototype = Object.assign(Object.create(Action), prototype); MapperAction.prototype.name = name; MapperAction.prototype.toString = mapperActionToString; Future.prototype[name] = function checkedMapperTransformation(mapper){ if(!isFuture(this)) throwInvalidContext('Future#' + name, this); if(!isFunction(mapper)) throwInvalidArgument('Future#' + name, 0, 'be a Function', mapper); return this[_name](mapper); }; Future.prototype[_name] = function uncheckedMapperTransformation(mapper){ return this._transform(new MapperAction( mapper, captureActionContext(name, uncheckedMapperTransformation) )); }; return MapperAction; } function bimapperActionToString(){ return this.name + '(' + showf(this.lmapper) + ', ' + showf(this.rmapper) + ')'; } function defineBimapperAction(name, prototype){ var _name = '_' + name; function BimapperAction(lmapper, rmapper, context){ this.lmapper = lmapper; this.rmapper = rmapper; this.context = context; } BimapperAction.prototype = Object.assign(Object.create(Action), prototype); BimapperAction.prototype.name = name; BimapperAction.prototype.toString = bimapperActionToString; Future.prototype[name] = function checkedBimapperTransformation(lm, rm){ if(!isFuture(this)) throwInvalidContext('Future#' + name, this); if(!isFunction(lm)) throwInvalidArgument('Future#' + name, 0, 'be a Function', lm); if(!isFunction(rm)) throwInvalidArgument('Future#' + name, 1, 'be a Function', rm); return this[_name](lm, rm); }; Future.prototype[_name] = function uncheckedBimapperTransformation(lmapper, rmapper){ return this._transform(new BimapperAction( lmapper, rmapper, captureActionContext(name, uncheckedBimapperTransformation) )); }; return BimapperAction; } function otherActionToString(){ return this.name + '(' + this.other.toString() + ')'; } function defineOtherAction(name, prototype){ var _name = '_' + name; function OtherAction(other, context){ this.other = other; this.context = context; } OtherAction.prototype = Object.assign(Object.create(Action), prototype); OtherAction.prototype.name = name; OtherAction.prototype.toString = otherActionToString; Future.prototype[name] = function checkedOtherTransformation(other){ if(!isFuture(this)) throwInvalidContext('Future#' + name, this); if(!isFuture(other)) throwInvalidFuture('Future#' + name, 0, other); return this[_name](other); }; Future.prototype[_name] = function uncheckedOtherTransformation(other){ return this._transform(new OtherAction( other, captureActionContext(name, uncheckedOtherTransformation) )); }; return OtherAction; } function defineParallelAction(name, rec, rej, res, prototype){ var ParallelAction = defineOtherAction(name, prototype); ParallelAction.prototype.run = function ParallelAction$run(early){ var eager = new Eager(this.other); var action = new ParallelAction(eager); function ParallelAction$early(m){ early(m, action); } action.context = captureContext( this.context, name + ' triggering a parallel Future', ParallelAction$run ); action.cancel = eager._interpret( function ParallelAction$rec(x){ rec(ParallelAction$early, x); }, function ParallelAction$rej(x){ rej(ParallelAction$early, x); }, function ParallelAction$res(x){ res(ParallelAction$early, x); } ); return action; }; return ParallelAction; } function apActionHandler(f){ return isFunction(f) ? this.other._map(function ApAction$resolved$mapper(x){ return f(x) }) : new Crashed(makeError(typeError( 'Future#' + this.name + '() expects its first argument to be a Future of a Function\n' + ' Actual: Future.of(' + show(f) + ')' ), null, this.context)); } function chainActionHandler(x){ var m; try{ m = this.mapper(x); }catch(e){ return new Crashed(makeError(e, null, this.context)) } return isFuture(m) ? m : new Crashed(makeError(invalidFuture( 'Future#' + this.name, 'the function it\'s given to return a Future', m, '\n From calling: ' + showf(this.mapper) + '\n With: ' + show(x) ), null, this.context)); } function returnOther(){ return this.other; } function mapWith(mapper, create, value, context){ var m; try{ m = create(mapper(value)); }catch(e){ m = new Crashed(makeError(e, null, context)); } return m; } function mapRight(value){ return mapWith(this.rmapper, resolve, value, this.context); } function earlyCrash(early, x){ early(new Crashed(x)); } function earlyReject(early, x){ early(new Rejected(x)); } function earlyResolve(early, x){ early(new Resolved(x)); } defineOtherAction('ap', { resolved: apActionHandler }); defineMapperAction('map', { resolved: function MapAction$resolved(x){ return mapWith(this.mapper, resolve, x, this.context); } }); defineBimapperAction('bimap', { resolved: mapRight, rejected: function BimapAction$rejected(x){ return mapWith(this.lmapper, reject, x, this.context); } }); defineMapperAction('chain', { resolved: chainActionHandler }); defineMapperAction('mapRej', { rejected: function MapRejAction$rejected(x){ return mapWith(this.mapper, reject, x, this.context); } }); defineMapperAction('chainRej', { rejected: chainActionHandler }); defineNullaryAction('swap', { rejected: Action.resolved, resolved: Action.rejected }); defineBimapperAction('fold', { resolved: mapRight, rejected: function FoldAction$rejected(x){ return mapWith(this.lmapper, resolve, x, this.context); } }); var finallyAction = { rejected: function FinallyAction$rejected(x){ return this.other._and(new Rejected(x)) }, resolved: function FinallyAction$resolved(x){ return this.other._and(new Resolved(x)) } }; defineOtherAction('finally', finallyAction); defineOtherAction('lastly', finallyAction); defineOtherAction('and', { resolved: returnOther }); var altAction = { rejected: returnOther }; defineOtherAction('or', altAction); defineOtherAction('alt', altAction); defineParallelAction('_parallelAp', earlyCrash, earlyReject, noop, { resolved: apActionHandler }); defineParallelAction('race', earlyCrash, earlyReject, earlyResolve, {}); defineParallelAction('both', earlyCrash, earlyReject, noop, { resolved: function BothAction$resolved(x){ return this.other._map(function BothAction$resolved$mapper(y){ return [x, y] }); } }); function Next(x){ return {done: false, value: x}; } function Done(x){ return {done: true, value: x}; } function isIteration(x){ return isObject(x) && isBoolean(x.done); } function chainRec(step, init){ return resolve(Next(init))._chain(function chainRec$recur(o){ return o.done ? resolve(o.value) : step(Next, Done, o.value)._chain(chainRec$recur); }); } function ap$mval(mval, mfunc){ if(!isApply(mfunc)) throwInvalidArgument('ap', 1, 'be an Apply', mfunc); return mfunc[FL.ap](mval); } function ap(mval, mfunc){ if(!isApply(mval)) throwInvalidArgument('ap', 0, 'be an Apply', mval); if(arguments.length === 1) return partial1(ap$mval, mval); return ap$mval(mval, mfunc); } function alt$left(left, right){ if(!isAlt(right)) throwInvalidArgument('alt', 1, 'be an Alt', right); return left[FL.alt](right); } function alt(left, right){ if(!isAlt(left)) throwInvalidArgument('alt', 0, 'be an Alt', left); if(arguments.length === 1) return partial1(alt$left, left); return alt$left(left, right); } function map$mapper(mapper, m){ if(!isFunctor(m)) throwInvalidArgument('map', 1, 'be a Functor', m); return m[FL.map](mapper); } function map(mapper, m){ if(!isFunction(mapper)) throwInvalidArgument('map', 0, 'be a Function', mapper); if(arguments.length === 1) return partial1(map$mapper, mapper); return map$mapper(mapper, m); } function bimap$lmapper$rmapper(lmapper, rmapper, m){ if(!isBifunctor(m)) throwInvalidArgument('bimap', 2, 'be a Bifunctor', m); return m[FL.bimap](lmapper, rmapper); } function bimap$lmapper(lmapper, rmapper, m){ if(!isFunction(rmapper)) throwInvalidArgument('bimap', 1, 'be a Function', rmapper); if(arguments.length === 2) return partial2(bimap$lmapper$rmapper, lmapper, rmapper); return bimap$lmapper$rmapper(lmapper, rmapper, m); } function bimap(lmapper, rmapper, m){ if(!isFunction(lmapper)) throwInvalidArgument('bimap', 0, 'be a Function', lmapper); if(arguments.length === 1) return partial1(bimap$lmapper, lmapper); if(arguments.length === 2) return bimap$lmapper(lmapper, rmapper); return bimap$lmapper(lmapper, rmapper, m); } function chain$chainer(chainer, m){ if(!isChain(m)) throwInvalidArgument('chain', 1, 'be a Chain', m); return m[FL.chain](chainer); } function chain(chainer, m){ if(!isFunction(chainer)) throwInvalidArgument('chain', 0, 'be a Function', chainer); if(arguments.length === 1) return partial1(chain$chainer, chainer); return chain$chainer(chainer, m); } function mapRej$mapper(mapper, m){ if(!isFuture(m)) throwInvalidFuture('mapRej', 1, m); return m.mapRej(mapper); } function mapRej(mapper, m){ if(!isFunction(mapper)) throwInvalidArgument('mapRej', 0, 'be a Function', mapper); if(arguments.length === 1) return partial1(mapRej$mapper, mapper); return mapRej$mapper(mapper, m); } function chainRej$chainer(chainer, m){ if(!isFuture(m)) throwInvalidFuture('chainRej', 1, m); return m.chainRej(chainer); } function chainRej(chainer, m){ if(!isFunction(chainer)) throwInvalidArgument('chainRej', 0, 'be a Function', chainer); if(arguments.length === 1) return partial1(chainRej$chainer, chainer); return chainRej$chainer(chainer, m); } function lastly$right(right, left){ if(!isFuture(left)) throwInvalidFuture('lastly', 1, left); return left.lastly(right); } function lastly(right, left){ if(!isFuture(right)) throwInvalidFuture('lastly', 0, right); if(arguments.length === 1) return partial1(lastly$right, right); return lastly$right(right, left); } function and$left(left, right){ if(!isFuture(right)) throwInvalidFuture('and', 1, right); return left.and(right); } function and(left, right){ if(!isFuture(left)) throwInvalidFuture('and', 0, left); if(arguments.length === 1) return partial1(and$left, left); return and$left(left, right); } function both$left(left, right){ if(!isFuture(right)) throwInvalidFuture('both', 1, right); return left.both(right); } function both(left, right){ if(!isFuture(left)) throwInvalidFuture('both', 0, left); if(arguments.length === 1) return partial1(both$left, left); return both$left(left, right); } function race$right(right, left){ if(!isFuture(left)) throwInvalidFuture('race', 1, left); return left.race(right); } function race(right, left){ if(!isFuture(right)) throwInvalidFuture('race', 0, right); if(arguments.length === 1) return partial1(race$right, right); return race$right(right, left); } function swap(m){ if(!isFuture(m)) throwInvalidFuture('swap', 0, m); return m.swap(); } function fold$f$g(f, g, m){ if(!isFuture(m)) throwInvalidFuture('fold', 2, m); return m.fold(f, g); } function fold$f(f, g, m){ if(!isFunction(g)) throwInvalidArgument('fold', 1, 'be a Function', g); if(arguments.length === 2) return partial2(fold$f$g, f, g); return fold$f$g(f, g, m); } function fold(f, g, m){ if(!isFunction(f)) throwInvalidArgument('fold', 0, 'be a Function', f); if(arguments.length === 1) return partial1(fold$f, f); if(arguments.length === 2) return fold$f(f, g); return fold$f(f, g, m); } function done$callback(callback, m){ if(!isFuture(m)) throwInvalidFuture('done', 1, m); return m.done(callback); } function done(callback, m){ if(!isFunction(callback)) throwInvalidArgument('done', 0, 'be a Function', callback); if(arguments.length === 1) return partial1(done$callback, callback); return done$callback(callback, m); } function fork$f$g(f, g, m){ if(!isFuture(m)) throwInvalidFuture('fork', 2, m); return m._interpret(raise, f, g); } function fork$f(f, g, m){ if(!isFunction(g)) throwInvalidArgument('fork', 1, 'be a Function', g); if(arguments.length === 2) return partial2(fork$f$g, f, g); return fork$f$g(f, g, m); } function fork(f, g, m){ if(!isFunction(f)) throwInvalidArgument('fork', 0, 'be a Function', f); if(arguments.length === 1) return partial1(fork$f, f); if(arguments.length === 2) return fork$f(f, g); return fork$f(f, g, m); } function forkCatch(f, g, h, m){ if(!isFunction(f)) throwInvalidArgument('forkCatch', 0, 'be a Function', f); if(arguments.length === 1) return partial1(forkCatch, f); if(!isFunction(g)) throwInvalidArgument('forkCatch', 1, 'be a Function', g); if(arguments.length === 2) return partial2(forkCatch, f, g); if(!isFunction(h)) throwInvalidArgument('forkCatch', 2, 'be a Function', h); if(arguments.length === 3) return partial3(forkCatch, f, g, h); if(!isFuture(m)) throwInvalidFuture('forkCatch', 3, m); return m._interpret(f, g, h); } function promise(m){ if(!isFuture(m)) throwInvalidFuture('promise', 0, m); return m.promise(); } function value$cont(cont, m){ if(!isFuture(m)) throwInvalidFuture('value', 1, m); return m.value(cont); } function value(cont, m){ if(!isFunction(cont)) throwInvalidArgument('value', 0, 'be a Function', cont); if(arguments.length === 1) return partial1(value$cont, cont); return value$cont(cont, m); } function extractLeft(m){ if(!isFuture(m)) throwInvalidFuture('extractLeft', 0, m); return m.extractLeft(); } function extractRight(m){ if(!isFuture(m)) throwInvalidFuture('extractRight', 0, m); return m.extractRight(); } function After(time, value){ this._time = time; this._value = value; this.context = captureContext(nil, 'a Future created with after', After); } After.prototype = Object.create(Future.prototype); After.prototype._interpret = function After$interpret(rec, rej, res){ var id = setTimeout(res, this._time, this._value); return function After$cancel(){ clearTimeout(id); }; }; After.prototype.extractRight = function After$extractRight(){ return [this._value]; }; After.prototype.toString = function After$toString(){ return 'after(' + show(this._time) + ', ' + show(this._value) + ')'; }; function RejectAfter(time, value){ this._time = time; this._value = value; this.context = captureContext(nil, 'a Future created with rejectAfter', After); } RejectAfter.prototype = Object.create(Future.prototype); RejectAfter.prototype._interpret = function RejectAfter$interpret(rec, rej){ var id = setTimeout(rej, this._time, this._value); return function RejectAfter$cancel(){ clearTimeout(id); }; }; RejectAfter.prototype.extractLeft = function RejectAfter$extractLeft(){ return [this._value]; }; RejectAfter.prototype.toString = function RejectAfter$toString(){ return 'rejectAfter(' + show(this._time) + ', ' + show(this._value) + ')'; }; function after$time(time, value){ return time === Infinity ? never : new After(time, value); } function after(time, value){ if(!isUnsigned(time)) throwInvalidArgument('after', 0, 'be a positive Integer', time); if(arguments.length === 1) return partial1(after$time, time); return after$time(time, value); } function rejectAfter$time(time, reason){ return time === Infinity ? never : new RejectAfter(time, reason); } function rejectAfter(time, reason){ if(!isUnsigned(time)){ throwInvalidArgument('rejectAfter', 0, 'be a positive Integer', time); } if(arguments.length === 1) return partial1(rejectAfter$time, time); return rejectAfter$time(time, reason); } function Attempt(fn){ this._fn = fn; this.context = captureContext(nil, 'a Future created with attempt/try', Attempt); } Attempt.prototype = Object.create(Future.prototype); Attempt.prototype._interpret = function Attempt$interpret(rec, rej, res){ var r; try{ r = this._fn(); }catch(e){ rej(e); return noop } res(r); return noop; }; Attempt.prototype.toString = function Attempt$toString(){ return 'attempt(' + showf(this._fn) + ')'; }; function attempt(f){ if(!isFunction(f)) throwInvalidArgument('attempt', 0, 'be a Function', f); return new Attempt(f); } var Cold = Cached.Cold = 0; var Pending = Cached.Pending = 1; var Crashed$1 = Cached.Crashed = 2; var Rejected$1 = Cached.Rejected = 3; var Resolved$1 = Cached.Resolved = 4; function Queued(rec, rej, res){ this[Crashed$1] = rec; this[Rejected$1] = rej; this[Resolved$1] = res; } function Cached(pure){ this._pure = pure; this.reset(); } Cached.prototype = Object.create(Future.prototype); Cached.prototype.extractLeft = function Cached$extractLeft(){ return this._state === Rejected$1 ? [this._value] : []; }; Cached.prototype.extractRight = function Cached$extractRight(){ return this._state === Resolved$1 ? [this._value] : []; }; Cached.prototype._addToQueue = function Cached$addToQueue(rec, rej, res){ var _this = this; if(_this._state > Pending) return noop; var i = _this._queue.push(new Queued(rec, rej, res)) - 1; _this._queued = _this._queued + 1; return function Cached$removeFromQueue(){ if(_this._state > Pending) return; _this._queue[i] = undefined; _this._queued = _this._queued - 1; if(_this._queued === 0) _this.reset(); }; }; Cached.prototype._drainQueue = function Cached$drainQueue(){ if(this._state <= Pending) return; if(this._queued === 0) return; var queue = this._queue; var length = queue.length; var state = this._state; var value = this._value; for(var i = 0; i < length; i++){ queue[i] && queue[i][state](value); queue[i] = undefined; } this._queue = undefined; this._queued = 0; }; Cached.prototype.crash = function Cached$crash(error){ if(this._state > Pending) return; this._value = error; this._state = Crashed$1; this._drainQueue(); }; Cached.prototype.reject = function Cached$reject(reason){ if(this._state > Pending) return; this._value = reason; this._state = Rejected$1; this._drainQueue(); }; Cached.prototype.resolve = function Cached$resolve(value){ if(this._state > Pending) return; this._value = value; this._state = Resolved$1; this._drainQueue(); }; Cached.prototype.run = function Cached$run(){ var _this = this; if(_this._state > Cold) return; _this._state = Pending; _this._cancel = _this._pure._interpret( function Cached$fork$rec(x){ _this.crash(x); }, function Cached$fork$rej(x){ _this.reject(x); }, function Cached$fork$res(x){ _this.resolve(x); } ); }; Cached.prototype.reset = function Cached$reset(){ if(this._state === Cold) return; if(this._state === Pending) this._cancel(); this._cancel = noop; this._queue = []; this._queued = 0; this._value = undefined; this._state = Cold; }; Cached.prototype._interpret = function Cached$interpret(rec, rej, res){ var cancel = noop; switch(this._state){ case Pending: cancel = this._addToQueue(rec, rej, res); break; case Crashed$1: rec(this._value); break; case Rejected$1: rej(this._value); break; case Resolved$1: res(this._value); break; default: cancel = this._addToQueue(rec, rej, res); this.run(); } return cancel; }; Cached.prototype.toString = function Cached$toString(){ return 'cache(' + this._pure.toString() + ')'; }; function cache(m){ if(!isFuture(m)) throwInvalidFuture('cache', 0, m); return new Cached(m); } function Encase(fn, a){ this._fn = fn; this._a = a; this.context = captureContext(nil, 'a Future created with encase', Encase); } Encase.prototype = Object.create(Future.prototype); Encase.prototype._interpret = function Encase$interpret(rec, rej, res){ var r; try{ r = this._fn(this._a); }catch(e){ rej(e); return noop } res(r); return noop; }; Encase.prototype.toString = function Encase$toString(){ return 'encase(' + showf(this._fn) + ', ' + show(this._a) + ')'; }; function encase(f, x){ if(!isFunction(f)) throwInvalidArgument('encase', 0, 'be a Function', f); if(arguments.length === 1) return partial1(encase, f); return new Encase(f, x); } function Encase2(fn, a, b){ this._fn = fn; this._a = a; this._b = b; this.context = captureContext(nil, 'a Future created with encase2', Encase2); } Encase2.prototype = Object.create(Future.prototype); Encase2.prototype._interpret = function Encase2$interpret(rec, rej, res){ var r; try{ r = this._fn(this._a, this._b); }catch(e){ rej(e); return noop } res(r); return noop; }; Encase2.prototype.toString = function Encase2$toString(){ return 'encase2(' + showf(this._fn) + ', ' + show(this._a) + ', ' + show(this._b) + ')'; }; function encase2(f, x, y){ if(!isFunction(f)) throwInvalidArgument('encase2', 0, 'be a Function', f); switch(arguments.length){ case 1: return partial1(encase2, f); case 2: return partial2(encase2, f, x); default: return new Encase2(f, x, y); } } function Encase3(fn, a, b, c){ this._fn = fn; this._a = a; this._b = b; this._c = c; this.context = captureContext(nil, 'a Future created with encase3', Encase3); } Encase3.prototype = Object.create(Future.prototype); Encase3.prototype._interpret = function Encase3$interpret(rec, rej, res){ var r; try{ r = this._fn(this._a, this._b, this._c); }catch(e){ rej(e); return noop } res(r); return noop; }; Encase3.prototype.toString = function Encase3$toString(){ return 'encase3(' + showf(this._fn) + ', ' + show(this._a) + ', ' + show(this._b) + ', ' + show(this._c) + ')'; }; function encase3(f, x, y, z){ if(!isFunction(f)) throwInvalidArgument('encase3', 0, 'be a Function', f); switch(arguments.length){ case 1: return partial1(encase3, f); case 2: return partial2(encase3, f, x); case 3: return partial3(encase3, f, x, y); default: return new Encase3(f, x, y, z); } } function EncaseN(fn, a){ this._fn = fn; this._a = a; this.context = captureContext(nil, 'a Future created with encaseN', EncaseN); } EncaseN.prototype = Object.create(Future.prototype); EncaseN.prototype._interpret = function EncaseN$interpret(rec, rej, res){ var open = false, cont = function(){ open = true; }; var context = captureContext(this.context, 'consuming an encased Future', EncaseN$interpret); try{ this._fn(this._a, function EncaseN$done(err, val){ cont = err ? function EncaseN3$rej(){ open = false; rej(err); } : function EncaseN3$res(){ open = false; res(val); }; if(open){ cont(); } }); }catch(e){ rec(makeError(e, this, context)); open = false; return noop; } cont(); return function EncaseN$cancel(){ open = false; }; }; EncaseN.prototype.toString = function EncaseN$toString(){ return 'encaseN(' + showf(this._fn) + ', ' + show(this._a) + ')'; }; function encaseN(f, x){ if(!isFunction(f)) throwInvalidArgument('encaseN', 0, 'be a Function', f); if(arguments.length === 1) return partial1(encaseN, f); return new EncaseN(f, x); } function EncaseN2(fn, a, b){ this._fn = fn; this._a = a; this._b = b; this.context = captureContext(nil, 'a Future created with encaseN2', EncaseN2); } EncaseN2.prototype = Object.create(Future.prototype); EncaseN2.prototype._interpret = function EncaseN2$interpret(rec, rej, res){ var open = false, cont = function(){ open = true; }; var context = captureContext(this.context, 'consuming an encased Future', EncaseN2$interpret); try{ this._fn(this._a, this._b, function EncaseN2$done(err, val){ cont = err ? function EncaseN2$rej(){ open = false; rej(err); } : function EncaseN2$res(){ open = false; res(val); }; if(open){ cont(); } }); }catch(e){ rec(makeError(e, this, context)); open = false; return noop; } cont(); return function EncaseN2$cancel(){ open = false; }; }; EncaseN2.prototype.toString = function EncaseN2$toString(){ return 'encaseN2(' + showf(this._fn) + ', ' + show(this._a) + ', ' + show(this._b) + ')'; }; function encaseN2(f, x, y){ if(!isFunction(f)) throwInvalidArgument('encaseN2', 0, 'be a Function', f); switch(arguments.length){ case 1: return partial1(encaseN2, f); case 2: return partial2(encaseN2, f, x); default: return new EncaseN2(f, x, y); } } function EncaseN3(fn, a, b, c){ this._fn = fn; this._a = a; this._b = b; this._c = c; this.context = captureContext(nil, 'a Future created with encaseN3', EncaseN3); } EncaseN3.prototype = Object.create(Future.prototype); EncaseN3.prototype._interpret = function EncaseN3$interpret(rec, rej, res){ var open = false, cont = function(){ open = true; }; var context = captureContext(this.context, 'consuming an encased Future', EncaseN3$interpret); try{ this._fn(this._a, this._b, this._c, function EncaseN3$done(err, val){ cont = err ? function EncaseN3$rej(){ open = false; rej(err); } : function EncaseN3$res(){ open = false; res(val); }; if(open){ cont(); } }); }catch(e){ rec(makeError(e, this, context)); open = false; return noop; } cont(); return function EncaseN3$cancel(){ open = fals