Comparing version 3.1.1 to 4.0.0
1687
fluture.js
@@ -14,10 +14,10 @@ //// ____ _ _ | ||
if(module && typeof module.exports !== 'undefined'){ | ||
module.exports = f(require('inspect-f')); | ||
module.exports = f(require('inspect-f'), require('sanctuary-type-classes')); | ||
} | ||
else{ | ||
global.Fluture = f(global.inspectf); | ||
global.Fluture = f(global.inspectf, global.sanctuaryTypeClasses); | ||
} | ||
}(/*istanbul ignore next*/(global || window || this), function(inspectf){ | ||
}(/*istanbul ignore next*/(global || window || this), function(inspectf, Z){ | ||
@@ -45,3 +45,3 @@ 'use strict'; | ||
function isFuture(m){ | ||
return (m instanceof FutureClass) || Boolean(m) && m['@@type'] === TYPEOF_FUTURE; | ||
return m instanceof Future || Boolean(m) && m['@@type'] === TYPEOF_FUTURE; | ||
} | ||
@@ -81,30 +81,4 @@ | ||
//A small string representing a value, but not containing the whole value. | ||
const preview = x => | ||
typeof x === 'string' | ||
? JSON.stringify(x) | ||
: Array.isArray(x) | ||
? `[Array: ${x.length}]` | ||
: typeof x === 'function' | ||
? typeof x.name === 'string' && x.name.length > 0 | ||
? `[Function: ${x.name}]` | ||
: /*istanbul ignore next*/ '[Function]' //Only for older JS engines. | ||
: x && typeof x === 'object' | ||
? `[Object: ${Object.keys(x).map(String).join(', ')}]` | ||
: String(x); | ||
//A show function to provide a slightly more meaningful representation of values. | ||
const show = x => | ||
typeof x === 'string' | ||
? preview(x) | ||
: Array.isArray(x) | ||
? `[${x.map(preview).join(', ')}]` | ||
: x && (typeof x.toString === 'function') | ||
? x.toString === Object.prototype.toString | ||
? `{${Object.keys(x).reduce((o, k) => o.concat(`${preview(k)}: ${preview(x[k])}`), []).join(', ')}}` | ||
: x.toString() | ||
: preview(x); | ||
const show = Z.toString; | ||
const noop = function noop(){}; | ||
const call = function call(f){f()}; | ||
const padf = (sf, s) => s.replace(/^/gm, sf).replace(sf, ''); | ||
@@ -138,6 +112,40 @@ const showf = f => padf(' ', inspectf(2, f)); | ||
//////////// | ||
// Errors // | ||
//////////// | ||
//Creates a dispatcher for a nullary method. | ||
function createNullaryDispatcher(method){ | ||
return function nullaryDispatch(m){ | ||
if(m && typeof m[method] === 'function') return m[method](); | ||
error$invalidArgument(`Future.${method}`, 1, `have a "${method}" method`, m); | ||
}; | ||
} | ||
//Creates a dispatcher for a unary method. | ||
function createUnaryDispatcher(method){ | ||
return function unaryDispatch(a, m){ | ||
if(arguments.length === 1) return unaryPartial(unaryDispatch, a); | ||
if(m && typeof m[method] === 'function') return m[method](a); | ||
error$invalidArgument(`Future.${method}`, 1, `have a "${method}" method`, m); | ||
}; | ||
} | ||
//Creates a dispatcher for a binary method. | ||
function createBinaryDispatcher(method){ | ||
return function binaryDispatch(a, b, m){ | ||
if(arguments.length === 1) return unaryPartial(binaryDispatch, a); | ||
if(arguments.length === 2) return binaryPartial(binaryDispatch, a, b); | ||
if(m && typeof m[method] === 'function') return m[method](a, b); | ||
error$invalidArgument(`Future.${method}`, 2, `have a "${method}" method`, m); | ||
}; | ||
} | ||
//Creates a dispatcher for a binary method, but takes the object first rather than last. | ||
function createInvertedBinaryDispatcher(method){ | ||
return function invertedBinaryDispatch(m, a, b){ | ||
if(arguments.length === 1) return unaryPartial(invertedBinaryDispatch, m); | ||
if(arguments.length === 2) return binaryPartial(invertedBinaryDispatch, m, a); | ||
if(m && typeof m[method] === 'function') return m[method](a, b); | ||
error$invalidArgument(`Future.${method}`, 0, `have a "${method}" method`, m); | ||
}; | ||
} | ||
//Creates an error about an invalid argument. | ||
function error$invalidArgument(it, at, expected, actual){ | ||
@@ -149,2 +157,3 @@ throw new TypeError( | ||
//Creates an error message about a method being called with an invalid context. | ||
function error$invalidContext(it, actual){ | ||
@@ -157,12 +166,355 @@ throw new TypeError( | ||
function check$Future(fork){ | ||
if(!isFunction(fork)) error$invalidArgument('Future', 0, 'be a function', fork); | ||
//////////////// | ||
// Base class // | ||
//////////////// | ||
function Future(f){ | ||
if(!isFunction(f)) error$invalidArgument('Future', 0, 'be a function', f); | ||
return new SafeFuture(f); | ||
} | ||
function check$fork(it, rej, res){ | ||
if(!isFuture(it)) error$invalidContext('Future#fork', it); | ||
function Future$of(x){ | ||
return new FutureOf(x); | ||
} | ||
function Future$chainRec(f, init){ | ||
if(arguments.length === 1) return unaryPartial(Future$chainRec, f); | ||
if(!isFunction(f)) error$invalidArgument('Future.chainRec', 0, 'be a function', f); | ||
return new ChainRec(f, init); | ||
} | ||
Future.prototype['@@type'] = TYPEOF_FUTURE; | ||
Future.prototype._f = null; | ||
Future.prototype.extractLeft = function Future$extractLeft(){ return []; } | ||
Future.prototype.extractRight = function Future$extractRight(){ return []; } | ||
Future.prototype.of = Future$of; | ||
Future.prototype.ap = function Future$ap(m){ | ||
if(!isFuture(this)) error$invalidContext('Future#ap', this); | ||
if(!isFuture(m)) error$invalidArgument('Future#ap', 0, 'be a Future', m); | ||
return new FutureAp(this, m); | ||
}; | ||
Future.prototype.map = function Future$map(f){ | ||
if(!isFuture(this)) error$invalidContext('Future#map', this); | ||
if(!isFunction(f)) error$invalidArgument('Future#map', 0, 'be a function', f); | ||
return new FutureMap(this, f); | ||
}; | ||
Future.prototype.bimap = function Future$bimap(f, g){ | ||
if(!isFuture(this)) error$invalidContext('Future#bimap', this); | ||
if(!isFunction(f)) error$invalidArgument('Future#bimap', 0, 'be a function', f); | ||
if(!isFunction(g)) error$invalidArgument('Future#bimap', 1, 'be a function', g); | ||
return new FutureBimap(this, f, g); | ||
}; | ||
Future.prototype.chain = function Future$chain(f){ | ||
if(!isFuture(this)) error$invalidContext('Future#chain', this); | ||
if(!isFunction(f)) error$invalidArgument('Future#chain', 0, 'be a function', f); | ||
return new FutureChain(this, f); | ||
}; | ||
Future.prototype.chainRej = function Future$chainRej(f){ | ||
if(!isFuture(this)) error$invalidContext('Future.chainRej', this); | ||
if(!isFunction(f)) error$invalidArgument('Future.chainRej', 0, 'a function', f); | ||
return new FutureChainRej(this, f); | ||
}; | ||
Future.prototype.mapRej = function Future$mapRej(f){ | ||
if(!isFuture(this)) error$invalidContext('Future#mapRej', this); | ||
if(!isFunction(f)) error$invalidArgument('Future#mapRej', 0, 'be a function', f); | ||
return new FutureMapRej(this, f); | ||
}; | ||
Future.prototype.swap = function Future$swap(){ | ||
if(!isFuture(this)) error$invalidContext('Future#swap', this); | ||
return new FutureSwap(this); | ||
}; | ||
Future.prototype.race = function Future$race(m){ | ||
if(!isFuture(this)) error$invalidContext('Future#race', this); | ||
if(!isFuture(m)) error$invalidArgument('Future#race', 0, 'be a Future', m); | ||
return new FutureRace(this, m); | ||
}; | ||
Future.prototype.and = function Future$and(m){ | ||
if(!isFuture(this)) error$invalidContext('Future#and', this); | ||
if(!isFuture(m)) error$invalidArgument('Future#and', 0, 'be a Future', m); | ||
return new FutureAnd(this, m); | ||
}; | ||
Future.prototype.or = function Future$or(m){ | ||
if(!isFuture(this)) error$invalidContext('Future#or', this); | ||
if(!isFuture(m)) error$invalidArgument('Future#or', 0, 'be a Future', m); | ||
return new FutureOr(this, m); | ||
}; | ||
Future.prototype.both = function Future$both(m){ | ||
if(!isFuture(this)) error$invalidContext('Future#both', this); | ||
if(!isFuture(m)) error$invalidArgument('Future#both', 0, 'be a Future', m); | ||
return new FutureBoth(this, m); | ||
}; | ||
Future.prototype.fold = function Future$fold(f, g){ | ||
if(!isFuture(this)) error$invalidContext('Future#fold', this); | ||
if(!isFunction(f)) error$invalidArgument('Future#fold', 0, 'be a function', f); | ||
if(!isFunction(g)) error$invalidArgument('Future#fold', 1, 'be a function', g); | ||
return new FutureFold(this, f, g); | ||
}; | ||
Future.prototype.hook = function Future$hook(dispose, consume){ | ||
if(!isFuture(this)) error$invalidContext('Future#hook', this); | ||
if(!isFunction(dispose)) error$invalidArgument('Future#hook', 0, 'be a function', dispose); | ||
if(!isFunction(consume)) error$invalidArgument('Future#hook', 1, 'be a function', consume); | ||
return new FutureHook(this, dispose, consume); | ||
}; | ||
Future.prototype.finally = function Future$finally(m){ | ||
if(!isFuture(this)) error$invalidContext('Future#finally', this); | ||
if(!isFuture(m)) error$invalidArgument('Future#finally', 0, 'be a Future', m); | ||
return new FutureFinally(this, m); | ||
}; | ||
Future.prototype.cache = function Future$cache(){ | ||
if(!isFuture(this)) error$invalidContext('Future#cache', this); | ||
return new CachedFuture(this); | ||
}; | ||
Future.prototype.fork = function Future$fork(rej, res){ | ||
if(!isFuture(this)) error$invalidContext('Future#fork', this); | ||
if(!isFunction(rej)) error$invalidArgument('Future#fork', 0, 'be a function', rej); | ||
if(!isFunction(res)) error$invalidArgument('Future#fork', 1, 'be a function', res); | ||
return this._f(rej, res); | ||
}; | ||
Future.prototype.inspect = function Future$inspect(){ | ||
return this.toString(); | ||
}; | ||
Future.prototype.value = function Future$value(f){ | ||
if(!isFuture(this)) error$invalidContext('Future#value', this); | ||
if(!isFunction(f)) error$invalidArgument('Future#value', 0, 'be a function', f); | ||
return this._f(function Future$value$rej(e){ | ||
throw new Error( | ||
`Future#value was called on a rejected Future\n Actual: Future.reject(${show(e)})` | ||
); | ||
}, f); | ||
}; | ||
Future.prototype.promise = function Future$promise(){ | ||
if(!isFuture(this)) error$invalidContext('Future#promise', this); | ||
const _this = this; | ||
return new Promise(function Future$promise$do(resolve, reject){ | ||
_this._f(reject, resolve); | ||
}); | ||
}; | ||
Future.of = Future$of; | ||
Future.chainRec = Future$chainRec; | ||
Future.Future = Future; | ||
Future.isFuture = isFuture; | ||
Future.isForkable = isForkable; | ||
function ap$mval(mval, mfunc){ | ||
if(!Z.Apply.test(mfunc)) error$invalidArgument('Future.ap', 1, 'be an Apply', mfunc); | ||
return Z.ap(mval, mfunc); | ||
} | ||
Future.ap = function ap(mval, mfunc){ | ||
if(!Z.Apply.test(mval)) error$invalidArgument('Future.ap', 0, 'be an Apply', mval); | ||
if(arguments.length === 1) return unaryPartial(ap$mval, mval); | ||
return ap$mval(mval, mfunc); | ||
} | ||
function map$mapper(mapper, m){ | ||
if(!Z.Functor.test(m)) error$invalidArgument('Future.map', 1, 'be a Functor', m); | ||
return Z.map(mapper, m); | ||
} | ||
Future.map = function map(mapper, m){ | ||
if(!isFunction(mapper)) error$invalidArgument('Future.map', 0, 'be a Function', mapper); | ||
if(arguments.length === 1) return unaryPartial(map$mapper, mapper); | ||
return map$mapper(mapper, m); | ||
} | ||
function bimap$lmapper$rmapper(lmapper, rmapper, m){ | ||
if(!Z.Bifunctor.test(m)) error$invalidArgument('Future.bimap', 2, 'be a Bifunctor', m); | ||
return Z.bimap(lmapper, rmapper, m); | ||
} | ||
function bimap$lmapper(lmapper, rmapper, m){ | ||
if(!isFunction(rmapper)) error$invalidArgument('Future.bimap', 1, 'be a Function', rmapper); | ||
if(arguments.length === 2) return binaryPartial(bimap$lmapper$rmapper, lmapper, rmapper); | ||
return bimap$lmapper$rmapper(lmapper, rmapper, m); | ||
} | ||
Future.bimap = function bimap(lmapper, rmapper, m){ | ||
if(!isFunction(lmapper)) error$invalidArgument('Future.bimap', 0, 'be a Function', lmapper); | ||
if(arguments.length === 1) return unaryPartial(bimap$lmapper, lmapper); | ||
return bimap$lmapper(lmapper, rmapper, m); | ||
} | ||
function chain$chainer(chainer, m){ | ||
if(!Z.Chain.test(m)) error$invalidArgument('Future.chain', 1, 'be a Chain', m); | ||
return Z.chain(chainer, m); | ||
} | ||
Future.chain = function chain(chainer, m){ | ||
if(!isFunction(chainer)) error$invalidArgument('Future.chain', 0, 'be a Function', chainer); | ||
if(arguments.length === 1) return unaryPartial(chain$chainer, chainer); | ||
return chain$chainer(chainer, m); | ||
} | ||
function and$left(left, right){ | ||
if(!isFuture(right)) error$invalidArgument('Future.and', 1, 'be a Future', right); | ||
return new FutureAnd(left, right); | ||
} | ||
Future.and = function and(left, right){ | ||
if(!isFuture(left)) error$invalidArgument('Future.and', 0, 'be a Future', left); | ||
if(arguments.length === 1) return unaryPartial(and$left, left); | ||
return and$left(left, right); | ||
} | ||
function both$left(left, right){ | ||
if(!isFuture(right)) error$invalidArgument('Future.both', 1, 'be a Future', right); | ||
return new FutureBoth(left, right); | ||
} | ||
Future.both = function both(left, right){ | ||
if(!isFuture(left)) error$invalidArgument('Future.both', 0, 'be a Future', left); | ||
if(arguments.length === 1) return unaryPartial(both$left, left); | ||
return both$left(left, right); | ||
} | ||
Future.reject = function Future$reject(x){ | ||
return new FutureReject(x); | ||
}; | ||
function Future$after$n(n, x){ | ||
return new FutureAfter(n, x); | ||
} | ||
Future.after = function Future$after(n, x){ | ||
if(!isPositiveInteger(n)) error$invalidArgument('Future.after', 0, 'be a positive integer', n); | ||
if(arguments.length === 1) return unaryPartial(Future$after$n, n); | ||
return Future$after$n(n, x); | ||
}; | ||
function rejectAfter$time(time, reason){ | ||
return new FutureRejectAfter(time, reason); | ||
} | ||
Future.rejectAfter = function rejectAfter(time, reason){ | ||
if(!isPositiveInteger(time)) error$invalidArgument( | ||
'Future.rejectAfter', 0, 'be a positive integer', time | ||
); | ||
if(arguments.length === 1) return unaryPartial(rejectAfter$time, time); | ||
return rejectAfter$time(time, reason); | ||
} | ||
Future.cast = function Future$cast(m){ | ||
if(!isForkable(m)) error$invalidArgument('Future.cast', 0, 'be a Forkable', m); | ||
return new FutureCast(m); | ||
}; | ||
Future.try = function Future$try(f){ | ||
if(!isFunction(f)) error$invalidArgument('Future.try', 0, 'be a function', f); | ||
return new FutureTry(f); | ||
}; | ||
Future.encase = function Future$encase(f, x){ | ||
if(arguments.length === 1) return unaryPartial(Future$encase, f); | ||
if(!isFunction(f)) error$invalidArgument('Future.encase', 0, 'be a function', f); | ||
return new FutureEncase(f, x); | ||
}; | ||
Future.encase2 = function Future$encase2(f, x, y){ | ||
switch(arguments.length){ | ||
case 1: return unaryPartial(Future$encase2, f); | ||
case 2: return binaryPartial(Future$encase2, f, x); | ||
default: | ||
if(!isFunction(f)) error$invalidArgument('Future.encase2', 0, 'be a function', f); | ||
if(!isBinary(f)) error$invalidArgument('Future.encase2', 0, 'take two arguments', f); | ||
return new FutureEncase(f, x, y); | ||
} | ||
}; | ||
Future.encase3 = function Future$encase3(f, x, y, z){ | ||
switch(arguments.length){ | ||
case 1: return unaryPartial(Future$encase3, f); | ||
case 2: return binaryPartial(Future$encase3, f, x); | ||
case 3: return ternaryPartial(Future$encase3, f, x, y); | ||
default: | ||
if(!isFunction(f)) error$invalidArgument('Future.encase3', 0, 'be a function', f); | ||
if(!isTernary(f)) error$invalidArgument('Future.encase3', 0, 'take three arguments', f); | ||
return new FutureEncase(f, x, y, z); | ||
} | ||
}; | ||
Future.node = function Future$node(f){ | ||
if(!isFunction(f)) error$invalidArgument('Future.node', 0, 'be a function', f); | ||
return new FutureNode(f); | ||
}; | ||
Future.parallel = function Future$parallel(i, ms){ | ||
if(arguments.length === 1) return unaryPartial(Future$parallel, i); | ||
if(!isPositiveInteger(i)) error$invalidArgument('Future.parallel', 0, 'be a positive integer', i); | ||
if(!Array.isArray(ms)) error$invalidArgument('Future.parallel', 1, 'be an array', ms); | ||
return new FutureParallel(i, ms); | ||
}; | ||
Future.do = function Future$do(f){ | ||
if(!isFunction(f)) error$invalidArgument('Future.do', 0, 'be a function', f); | ||
return new FutureDo(f); | ||
}; | ||
Future.chainRej = createUnaryDispatcher('chainRej'); | ||
Future.mapRej = createUnaryDispatcher('mapRej'); | ||
Future.swap = createNullaryDispatcher('swap'); | ||
Future.fork = createBinaryDispatcher('fork'); | ||
Future.race = createUnaryDispatcher('race'); | ||
Future.or = createUnaryDispatcher('or'); | ||
Future.fold = createBinaryDispatcher('fold'); | ||
Future.hook = createInvertedBinaryDispatcher('hook'); | ||
Future.finally = createUnaryDispatcher('finally'); | ||
Future.value = createUnaryDispatcher('value'); | ||
Future.promise = createNullaryDispatcher('promise'); | ||
Future.cache = createNullaryDispatcher('cache'); | ||
Future.extractLeft = createNullaryDispatcher('extractLeft'); | ||
Future.extractRight = createNullaryDispatcher('extractRight'); | ||
//Utilities. | ||
Future.util = { | ||
Next, | ||
Done, | ||
isForkable, | ||
isFuture, | ||
isFunction, | ||
isBinary, | ||
isTernary, | ||
isPositiveInteger, | ||
isObject, | ||
isIterator, | ||
isIteration, | ||
padf, | ||
showf, | ||
fid, | ||
unaryPartial, | ||
binaryPartial, | ||
ternaryPartial | ||
}; | ||
//Fantasy-Land compatibility. | ||
Future[FL.of] = Future.of; | ||
Future[FL.chainRec] = Future$chainRec; | ||
Future.prototype[FL.ap] = Future.prototype.ap; | ||
Future.prototype[FL.map] = Future.prototype.map; | ||
Future.prototype[FL.bimap] = Future.prototype.bimap; | ||
Future.prototype[FL.chain] = Future.prototype.chain; | ||
///////////////// | ||
// Sub classes // | ||
///////////////// | ||
function check$fork$f(f, c){ | ||
@@ -175,11 +527,39 @@ if(!(f === undefined || (isFunction(f) && f.length === 0))) throw new TypeError( | ||
function check$chain(it, f){ | ||
if(!isFuture(it)) error$invalidContext('Future#chain', it); | ||
if(!isFunction(f)) error$invalidArgument('Future#chain', 0, 'be a function', f); | ||
function SafeFuture(computation){ | ||
this._computation = computation; | ||
} | ||
function check$chainRec(f){ | ||
if(!isFunction(f)) error$invalidArgument('Future.chainRec', 0, 'be a function', f); | ||
SafeFuture.prototype = Object.create(Future.prototype); | ||
SafeFuture.prototype._f = function SafeFuture$fork(rej, res){ | ||
let open = true; | ||
const f = this._computation(function SafeFuture$fork$rej(x){ | ||
if(open){ | ||
open = false; | ||
rej(x); | ||
} | ||
}, function SafeFuture$fork$res(x){ | ||
if(open){ | ||
open = false; | ||
res(x); | ||
} | ||
}); | ||
check$fork$f(f, this._computation); | ||
return function SafeFuture$fork$cancel(){ | ||
open && f && f(); | ||
open = false; | ||
}; | ||
} | ||
SafeFuture.prototype.toString = function SafeFuture$toString(){ | ||
return `Future(${showf(this._computation)})`; | ||
} | ||
//---------- | ||
//data Timing = Undetermined | Synchronous | Asynchronous | ||
const Undetermined = 0; | ||
const Synchronous = 1; | ||
const Asynchronous = 2; | ||
function check$chainRec$f(m, f, i, x){ | ||
@@ -208,150 +588,282 @@ if(!isFuture(m)) throw new TypeError( | ||
function check$recur(it, f){ | ||
if(!isFuture(it)) error$invalidContext('Future#recur', it); | ||
if(!isFunction(f)) error$invalidArgument('Future#recur', 0, 'be a function', f); | ||
function ChainRec(iterate, init){ | ||
this._iterate = iterate; | ||
this._init = init; | ||
} | ||
function check$chain$f(m, f, x){ | ||
if(!isFuture(m)) throw new TypeError( | ||
'Future#chain expects the function its given to return a Future' | ||
+ `\n Actual: ${show(m)}\n From calling: ${showf(f)}\n With: ${show(x)}` | ||
); | ||
ChainRec.prototype = Object.create(Future.prototype); | ||
ChainRec.prototype._f = function ChainRec$fork(rej, res){ | ||
const _this = this; | ||
let cancel = noop, i = 0; | ||
(function Future$chainRec$recur(state){ | ||
let timing = Undetermined; | ||
function Future$chainRec$res(it){ | ||
check$chainRec$it(it, i); | ||
i = i + 1; | ||
if(timing === Undetermined){ | ||
timing = Synchronous; | ||
state = it; | ||
}else{ | ||
Future$chainRec$recur(it); | ||
} | ||
} | ||
while(!state.done){ | ||
timing = Undetermined; | ||
const m = _this._iterate(Next, Done, state.value); | ||
check$chainRec$f(m, _this._iterate, i, state.value); | ||
cancel = m._f(rej, Future$chainRec$res); | ||
if(timing !== Synchronous){ | ||
timing = Asynchronous; | ||
return; | ||
} | ||
} | ||
res(state.value); | ||
}(Next(_this._init))); | ||
return function Future$chainRec$cancel(){ cancel() }; | ||
} | ||
function check$recur$f(m, f, x){ | ||
if(!isFuture(m)) throw new TypeError( | ||
'Future#recur expects the function its given to return a Future' | ||
+ `\n Actual: ${show(m)}\n From calling: ${showf(f)}\n With: ${show(x)}` | ||
); | ||
ChainRec.prototype.toString = function ChainRec$toString(){ | ||
return `Future.chainRec(${showf(this._iterate)}, ${show(this._init)})`; | ||
} | ||
function check$chainRej(it, f){ | ||
if(!isFuture(it)) error$invalidContext('Future.chainRej', it); | ||
if(!isFunction(f)) error$invalidArgument('Future.chainRej', 0, 'a function', f); | ||
//---------- | ||
//data State = Cold | Pending | Rejected | Resolved | ||
const Cold = 0; | ||
const Pending = 1; | ||
const Rejected = 2; | ||
const Resolved = 3; | ||
function Queued(rej, res){ | ||
this[Rejected] = rej; | ||
this[Resolved] = res; | ||
} | ||
function check$chainRej$f(m, f, x){ | ||
if(!isFuture(m)) throw new TypeError( | ||
'Future.chainRej expects the function its given to return a Future' | ||
+ `\n Actual: ${show(m)}\n From calling: ${showf(f)}\n With: ${show(x)}` | ||
); | ||
function CachedFuture(pure){ | ||
this._pure = pure; | ||
this._cancel = noop; | ||
this._queue = []; | ||
this._queued = 0; | ||
this._value = null; | ||
this._state = Cold; | ||
} | ||
function check$map(it, f){ | ||
if(!isFuture(it)) error$invalidContext('Future#map', it); | ||
if(!isFunction(f)) error$invalidArgument('Future#map', 0, 'be a function', f); | ||
CachedFuture.STATE = { | ||
[Cold]: 'cold', | ||
[Pending]: 'pending', | ||
[Rejected]: 'rejected', | ||
[Resolved]: 'resolved' | ||
}; | ||
CachedFuture.prototype = Object.create(Future.prototype); | ||
CachedFuture.prototype.extractLeft = function CachedFuture$extractLeft(){ | ||
return this._state === Rejected ? [this._value] : []; | ||
} | ||
function check$mapRej(it, f){ | ||
if(!isFuture(it)) error$invalidContext('Future#mapRej', it); | ||
if(!isFunction(f)) error$invalidArgument('Future#mapRej', 0, 'be a function', f); | ||
CachedFuture.prototype.extractRight = function CachedFuture$extractRight(){ | ||
return this._state === Resolved ? [this._value] : []; | ||
} | ||
function check$bimap(it, f, g){ | ||
if(!isFuture(it)) error$invalidContext('Future#bimap', it); | ||
if(!isFunction(f)) error$invalidArgument('Future#bimap', 0, 'be a function', f); | ||
if(!isFunction(g)) error$invalidArgument('Future#bimap', 1, 'be a function', g); | ||
CachedFuture.prototype._addToQueue = function CachedFuture$addToQueue(rej, res){ | ||
const _this = this; | ||
if(_this._state > Pending) return noop; | ||
const i = _this._queue.push(new Queued(rej, res)) - 1; | ||
_this._queued = _this._queued + 1; | ||
return function CachedFuture$removeFromQueue(){ | ||
if(_this._state > Pending) return; | ||
_this._queue[i] = undefined; | ||
_this._queued = _this._queued - 1; | ||
if(_this._queued === 0) _this.reset(); | ||
}; | ||
} | ||
function check$ap(it, m){ | ||
if(!isFuture(it)) error$invalidContext('Future#ap', it); | ||
if(!isFuture(m)) error$invalidArgument('Future#ap', 0, 'be a Future', m); | ||
CachedFuture.prototype._drainQueue = function CachedFuture$drainQueue(){ | ||
if(this._state <= Pending) return; | ||
if(this._queued === 0) return; | ||
const queue = this._queue; | ||
const length = queue.length; | ||
const state = this._state; | ||
const value = this._value; | ||
for(let i = 0; i < length; i++){ | ||
queue[i] && queue[i][state](value); | ||
queue[i] = undefined; | ||
} | ||
this._queue = undefined; | ||
this._queued = 0; | ||
} | ||
function check$ap$f(f){ | ||
if(!isFunction(f)) throw new TypeError( | ||
'Future#ap expects its first argument to be a Future of a Function' | ||
+ `\n Actual: Future.of(${show(f)})` | ||
CachedFuture.prototype.reject = function CachedFuture$reject(reason){ | ||
if(this._state > Pending) return; | ||
this._value = reason; | ||
this._state = Rejected; | ||
this._drainQueue(); | ||
} | ||
CachedFuture.prototype.resolve = function CachedFuture$resolve(value){ | ||
if(this._state > Pending) return; | ||
this._value = value; | ||
this._state = Resolved; | ||
this._drainQueue(); | ||
} | ||
CachedFuture.prototype.run = function CachedFuture$run(){ | ||
const _this = this; | ||
if(_this._state > Cold) return; | ||
_this._state = Pending; | ||
_this._cancel = _this._pure._f( | ||
function CachedFuture$fork$rej(x){ _this.reject(x) }, | ||
function CachedFuture$fork$res(x){ _this.resolve(x) } | ||
); | ||
} | ||
function check$swap(it){ | ||
if(!isFuture(it)) error$invalidContext('Future#swap', it); | ||
CachedFuture.prototype.reset = function CachedFuture$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; | ||
} | ||
function check$race(it, m){ | ||
if(!isFuture(it)) error$invalidContext('Future#race', it); | ||
if(!isFuture(m)) error$invalidArgument('Future#race', 0, 'be a Future', m); | ||
CachedFuture.prototype.getState = function CachedFuture$getState(){ | ||
return CachedFuture.STATE[this._state]; | ||
} | ||
function check$or(it, m){ | ||
if(!isFuture(it)) error$invalidContext('Future#or', it); | ||
if(!isFuture(m)) error$invalidArgument('Future#or', 0, 'be a Future', m); | ||
CachedFuture.prototype._f = function CachedFuture$fork(rej, res){ | ||
const _this = this; | ||
let cancel = noop; | ||
switch(_this._state){ | ||
case 1: cancel = _this._addToQueue(rej, res); break; | ||
case 2: rej(_this._value); break; | ||
case 3: res(_this._value); break; | ||
default: cancel = _this._addToQueue(rej, res); _this.run(); | ||
} | ||
return cancel; | ||
} | ||
function check$fold(it, f, g){ | ||
if(!isFuture(it)) error$invalidContext('Future#fold', it); | ||
if(!isFunction(f)) error$invalidArgument('Future#fold', 0, 'be a function', f); | ||
if(!isFunction(g)) error$invalidArgument('Future#fold', 1, 'be a function', g); | ||
CachedFuture.prototype.inspect = function CachedFuture$inspect(){ | ||
const repr = this._state === Resolved | ||
? show(this._value) | ||
: `<${this.getState()}>` + (this._state === Rejected ? ` ${this._value}` : ''); | ||
return `CachedFuture({ ${repr} })`; | ||
} | ||
function check$hook(it, f, g){ | ||
if(!isFuture(it)) error$invalidContext('Future#hook', it); | ||
if(!isFunction(f)) error$invalidArgument('Future#hook', 0, 'be a function', f); | ||
if(!isFunction(g)) error$invalidArgument('Future#hook', 1, 'be a function', g); | ||
CachedFuture.prototype.toString = function CachedFuture$toString(){ | ||
return `${this._pure.toString()}.cache()`; | ||
} | ||
function check$hook$f(m, f, x){ | ||
if(!isFuture(m)) throw new TypeError( | ||
'Future#hook expects the first function its given to return a Future' | ||
+ `\n Actual: ${show(m)}\n From calling: ${showf(f)}\n With: ${show(x)}` | ||
); | ||
//---------- | ||
function FutureOf(value){ | ||
this._value = value; | ||
} | ||
function check$hook$g(m, g, x){ | ||
if(!isFuture(m)) throw new TypeError( | ||
'Future#hook expects the second function its given to return a Future' | ||
+ `\n Actual: ${show(m)}\n From calling: ${showf(g)}\n With: ${show(x)}` | ||
); | ||
FutureOf.prototype = Object.create(Future.prototype); | ||
FutureOf.prototype.extractRight = function FutureOf$extractRight(){ | ||
return [this._value]; | ||
} | ||
function check$finally(it, m){ | ||
if(!isFuture(it)) error$invalidContext('Future#finally', it); | ||
if(!isFuture(m)) error$invalidArgument('Future#finally', 0, 'be a Future', m); | ||
FutureOf.prototype._f = function FutureOf$fork(rej, res){ | ||
res(this._value); | ||
return noop; | ||
} | ||
function check$value(it, f){ | ||
if(!isFuture(it)) error$invalidContext('Future#value', it); | ||
if(!isFunction(f)) error$invalidArgument('Future#value', 0, 'be a function', f); | ||
FutureOf.prototype.toString = function FutureOf$toString(){ | ||
return `Future.of(${show(this._value)})`; | ||
} | ||
function check$promise(it){ | ||
if(!isFuture(it)) error$invalidContext('Future#promise', it); | ||
//---------- | ||
function FutureReject(reason){ | ||
this._reason = reason; | ||
} | ||
function check$cache(it){ | ||
if(!isFuture(it)) error$invalidContext('Future#cache', it); | ||
FutureReject.prototype = Object.create(Future.prototype); | ||
FutureReject.prototype.extractLeft = function FutureReject$extractLeft(){ | ||
return [this._reason]; | ||
} | ||
function check$after(n){ | ||
if(typeof n !== 'number') error$invalidArgument('Future.after', 0, 'be a number', n); | ||
FutureReject.prototype._f = function FutureReject$fork(rej){ | ||
rej(this._reason); | ||
return noop; | ||
} | ||
function check$cast(m){ | ||
if(!isForkable(m)) error$invalidArgument('Future.cast', 0, 'be a Forkable', m); | ||
FutureReject.prototype.toString = function FutureReject$toString(){ | ||
return `Future.reject(${show(this._reason)})`; | ||
} | ||
function check$encase(f){ | ||
if(!isFunction(f)) error$invalidArgument('Future.encase', 0, 'be a function', f); | ||
//---------- | ||
function FutureNode(computation){ | ||
this._computation = computation; | ||
} | ||
function check$encase2(f){ | ||
if(!isFunction(f)) error$invalidArgument('Future.encase2', 0, 'be a function', f); | ||
if(!isBinary(f)) error$invalidArgument('Future.encase2', 0, 'take two arguments', f); | ||
FutureNode.prototype = Object.create(Future.prototype); | ||
FutureNode.prototype._f = function FutureNode$fork(rej, res){ | ||
let open = true; | ||
this._computation(function FutureNode$fork$done(a, b){ | ||
if(open){ | ||
a ? rej(a) : res(b); | ||
open = false; | ||
} | ||
}); | ||
return function FutureNode$fork$cancel(){ open = false }; | ||
} | ||
function check$encase3(f){ | ||
if(!isFunction(f)) error$invalidArgument('Future.encase3', 0, 'be a function', f); | ||
if(!isTernary(f)) error$invalidArgument('Future.encase3', 0, 'take three arguments', f); | ||
FutureNode.prototype.toString = function FutureNode$toString(){ | ||
return `Future.node(${showf(this._computation)})`; | ||
} | ||
function check$node(f){ | ||
if(!isFunction(f)) error$invalidArgument('Future.node', 0, 'be a function', f); | ||
//---------- | ||
function FutureAfter(time, value){ | ||
this._time = time; | ||
this._value = value; | ||
} | ||
function check$parallel(i, ms){ | ||
if(!isPositiveInteger(i)) error$invalidArgument('Future.parallel', 0, 'be a positive integer', i); | ||
if(!Array.isArray(ms)) error$invalidArgument('Future.parallel', 1, 'be an array', ms); | ||
FutureAfter.prototype = Object.create(Future.prototype); | ||
FutureAfter.prototype.extractRight = function FutureAfter$extractRight(){ | ||
return [this._value]; | ||
} | ||
FutureAfter.prototype._f = function FutureAfter$fork(rej, res){ | ||
const id = setTimeout(res, this._time, this._value); | ||
return function FutureAfter$fork$cancel(){ clearTimeout(id) }; | ||
} | ||
FutureAfter.prototype.toString = function FutureAfter$toString(){ | ||
return `Future.after(${show(this._time)}, ${show(this._value)})`; | ||
} | ||
//---------- | ||
function FutureRejectAfter(time, reason){ | ||
this._time = time; | ||
this._reason = reason; | ||
} | ||
FutureRejectAfter.prototype = Object.create(Future.prototype); | ||
FutureRejectAfter.prototype.extractLeft = function FutureRejectAfter$extractLeft(){ | ||
return [this._reason]; | ||
} | ||
FutureRejectAfter.prototype._f = function FutureRejectAfter$fork(rej){ | ||
const id = setTimeout(rej, this._time, this._reason); | ||
return function FutureRejectAfter$fork$cancel(){ clearTimeout(id) }; | ||
} | ||
FutureRejectAfter.prototype.toString = function FutureRejectAfter$toString(){ | ||
return `Future.rejectAfter(${show(this._time)}, ${show(this._reason)})`; | ||
} | ||
//---------- | ||
function check$parallel$m(m, i){ | ||
@@ -364,6 +876,43 @@ if(!isFuture(m)) throw new TypeError( | ||
function check$do(f){ | ||
if(!isFunction(f)) error$invalidArgument('Future.do', 0, 'be a function', f); | ||
function FutureParallel$emptyFork(rej, res){ | ||
res([]); | ||
} | ||
function FutureParallel(max, futures){ | ||
this._futures = futures; | ||
this._length = futures.length; | ||
this._max = Math.min(this._length, max); | ||
if(futures.length === 0) this._f = FutureParallel$emptyFork; | ||
} | ||
FutureParallel.prototype = Object.create(Future.prototype); | ||
FutureParallel.prototype._f = function FutureParallel$fork(rej, res){ | ||
const _this = this, cancels = new Array(_this._max), out = new Array(_this._length); | ||
let i = _this._max, ok = 0; | ||
const cancelAll = function Future$parallel$cancel(){ | ||
for(let n = 0; n < _this._max; n++) cancels[n] && cancels[n](); | ||
}; | ||
const run = function FutureParallel$fork$run(future, j, c){ | ||
check$parallel$m(future, j); | ||
cancels[c] = future._f(function Future$parallel$fork$rej(reason){ | ||
cancelAll(); | ||
rej(reason); | ||
}, function Future$parallel$fork$res(value){ | ||
out[j] = value; | ||
ok = ok + 1; | ||
if(i < _this._length) run(_this._futures[i], i++, c); | ||
else if(ok === _this._length) res(out); | ||
}); | ||
} | ||
for(let n = 0; n < _this._max; n++) run(_this._futures[n], n, n); | ||
return cancelAll; | ||
} | ||
FutureParallel.prototype.toString = function FutureParallel$toString(){ | ||
return `Future.parallel(${show(this._max)}, [${this._futures.map(show).join(', ')}])`; | ||
} | ||
//---------- | ||
function check$do$g(g){ | ||
@@ -388,539 +937,505 @@ if(!isIterator(g)) error$invalidArgument( | ||
//////////// | ||
// Future // | ||
//////////// | ||
function FutureDo(generator){ | ||
this._generator = generator; | ||
} | ||
function FutureClass(safe, f){ | ||
this._f = safe === true ? Future$safeFork : f; | ||
this._raw = f; | ||
FutureDo.prototype = Object.create(Future.prototype); | ||
FutureDo.prototype._f = function FutureDo$fork(rej, res){ | ||
const iterator = this._generator(); | ||
check$do$g(iterator); | ||
const recurser = new ChainRec(function Future$do$next(next, _, x){ | ||
const iteration = iterator.next(x); | ||
check$do$next(iteration); | ||
return iteration.done ? new FutureOf(iteration) : iteration.value.map(next); | ||
}, undefined); | ||
return recurser._f(rej, res); | ||
} | ||
function Future(f){ | ||
check$Future(f); | ||
return new FutureClass(true, f); | ||
FutureDo.prototype.toString = function FutureDo$toString(){ | ||
return `Future.do(${showf(this._generator)})`; | ||
} | ||
function Future$of(x){ | ||
return new FutureClass(false, function Future$of$fork(rej, res){ | ||
res(x); | ||
//---------- | ||
function FutureCast(forkable){ | ||
SafeFuture.call(this, (l, r) => forkable.fork(l, r)); | ||
this._forkable = forkable; | ||
} | ||
FutureCast.prototype = Object.create(SafeFuture.prototype); | ||
FutureCast.prototype.toString = function FutureCast$toString(){ | ||
return `Future.cast(${show(this._forkable)})`; | ||
} | ||
//---------- | ||
function FutureTry(fn){ | ||
this._fn = fn; | ||
} | ||
FutureTry.prototype = Object.create(Future.prototype); | ||
FutureTry.prototype._f = function FutureTry$0$fork(rej, res){ | ||
let r; | ||
try{ r = this._fn() }catch(e){ rej(e); return noop } | ||
res(r); | ||
return noop; | ||
} | ||
FutureTry.prototype.toString = function FutureTry$toString(){ | ||
return `Future.try(${show(this._fn)})`; | ||
} | ||
//---------- | ||
function FutureEncase(fn, a, b, c){ | ||
this._length = arguments.length - 1; | ||
this._fn = fn; | ||
this._a = a; | ||
this._b = b; | ||
this._c = c; | ||
this._f = FutureEncase.FS[this._length]; | ||
} | ||
FutureEncase.FS = { | ||
1: function FutureEncase$1$fork(rej, res){ | ||
let r; | ||
try{ r = this._fn(this._a) }catch(e){ rej(e); return noop } | ||
res(r); | ||
return noop; | ||
}); | ||
}, | ||
2: function FutureEncase$2$fork(rej, res){ | ||
let r; | ||
try{ r = this._fn(this._a, this._b) }catch(e){ rej(e); return noop } | ||
res(r); | ||
return noop; | ||
}, | ||
3: function FutureEncase$3$fork(rej, res){ | ||
let r; | ||
try{ r = this._fn(this._a, this._b, this._c) }catch(e){ rej(e); return noop } | ||
res(r); | ||
return noop; | ||
} | ||
} | ||
function Future$chainRec(f, init){ | ||
if(arguments.length === 1) return unaryPartial(Future$chainRec, f); | ||
check$chainRec(f); | ||
return new FutureClass(false, function(rej, res){ | ||
let cancel = noop, i = 0; | ||
(function Future$chainRec$recur(state){ | ||
let isSync = null; | ||
function Future$chainRec$res(it){ | ||
check$chainRec$it(it, i); | ||
i = i + 1; | ||
if(isSync === null){ | ||
isSync = true; | ||
state = it; | ||
}else{ | ||
Future$chainRec$recur(it); | ||
} | ||
} | ||
while(!state.done){ | ||
isSync = null; | ||
const m = f(Next, Done, state.value); | ||
check$chainRec$f(m, f, i, state.value); | ||
cancel = m._f(rej, Future$chainRec$res); | ||
if(isSync === true){ | ||
continue; | ||
}else{ | ||
isSync = false; | ||
return; | ||
} | ||
} | ||
res(state.value); | ||
}(Next(init))); | ||
return function Future$chainRec$cancel(){ cancel() }; | ||
}); | ||
FutureEncase.prototype = Object.create(Future.prototype); | ||
FutureEncase.prototype.toString = function FutureEncase$toString(){ | ||
const args = [this._a, this._b, this._c].slice(0, this._length).map(show).join(', '); | ||
const name = `encase${this._length > 1 ? this._length : ''}`; | ||
return `Future.${name}(${show(this._fn)}, ${args})`; | ||
} | ||
function Future$fork(rej, res){ | ||
check$fork(this, rej, res); | ||
return this._f(rej, res); | ||
//---------- | ||
function check$chain$f(m, f, x){ | ||
if(!isFuture(m)) throw new TypeError( | ||
'Future#chain expects the function its given to return a Future' | ||
+ `\n Actual: ${show(m)}\n From calling: ${showf(f)}\n With: ${show(x)}` | ||
); | ||
} | ||
function Future$safeFork(rej, res){ | ||
let open = true; | ||
const f = this._raw(function Future$safeFork$rej(x){ | ||
if(open){ | ||
open = false; | ||
rej(x); | ||
} | ||
}, function Future$safeFork$res(x){ | ||
if(open){ | ||
open = false; | ||
res(x); | ||
} | ||
}); | ||
check$fork$f(f, this._raw); | ||
return function Future$safeFork$cancel(){ | ||
open && f && f(); | ||
open = false; | ||
}; | ||
function FutureChain(parent, chainer){ | ||
this._parent = parent; | ||
this._chainer = chainer; | ||
} | ||
function Future$chain(f){ | ||
check$chain(this, f); | ||
FutureChain.prototype = Object.create(Future.prototype); | ||
FutureChain.prototype._f = function FutureChain$fork(rej, res){ | ||
const _this = this; | ||
return new FutureClass(false, function Future$chain$fork(rej, res){ | ||
let cancel; | ||
const r = _this._f(rej, function Future$chain$res(x){ | ||
const m = f(x); | ||
check$chain$f(m, f, x); | ||
cancel = m._f(rej, res); | ||
}); | ||
return cancel ? cancel : (cancel = r, function Future$chain$cancel(){ cancel() }); | ||
let cancel; | ||
const r = _this._parent._f(rej, function FutureChain$fork$res(x){ | ||
const m = _this._chainer(x); | ||
check$chain$f(m, _this._chainer, x); | ||
cancel = m._f(rej, res); | ||
}); | ||
return cancel || (cancel = r, function FutureChain$fork$cancel(){ cancel() }); | ||
} | ||
function Future$recur(f){ | ||
check$recur(this, f); | ||
const _this = this; | ||
return new FutureClass(false, function Future$chain$fork(rej, res){ | ||
return _this._f(rej, function Future$chain$res(x){ | ||
const m = f(x); | ||
check$recur$f(m, f, x); | ||
m._f(rej, res); | ||
}); | ||
}); | ||
FutureChain.prototype.toString = function FutureChain$toString(){ | ||
return `${this._parent.toString()}.chain(${showf(this._chainer)})`; | ||
} | ||
function Future$chainRej(f){ | ||
check$chainRej(this, f); | ||
const _this = this; | ||
return new FutureClass(false, function Future$chainRej$fork(rej, res){ | ||
let cancel; | ||
const r = _this._f(function Future$chainRej$rej(x){ | ||
const m = f(x); | ||
check$chainRej$f(m, f, x); | ||
cancel = m._f(rej, res); | ||
}, res); | ||
return cancel ? cancel : (cancel = r, function Future$chainRej$cancel(){ cancel() }); | ||
}); | ||
//---------- | ||
function check$chainRej$f(m, f, x){ | ||
if(!isFuture(m)) throw new TypeError( | ||
'Future.chainRej expects the function its given to return a Future' | ||
+ `\n Actual: ${show(m)}\n From calling: ${showf(f)}\n With: ${show(x)}` | ||
); | ||
} | ||
function Future$map(f){ | ||
check$map(this, f); | ||
const _this = this; | ||
return new FutureClass(false, function Future$map$fork(rej, res){ | ||
return _this._f(rej, function Future$map$res(x){ | ||
res(f(x)); | ||
}); | ||
}); | ||
function FutureChainRej(parent, chainer){ | ||
this._parent = parent; | ||
this._chainer = chainer; | ||
} | ||
function Future$mapRej(f){ | ||
check$mapRej(this, f); | ||
FutureChainRej.prototype = Object.create(Future.prototype); | ||
FutureChainRej.prototype._f = function FutureChainRej$fork(rej, res){ | ||
const _this = this; | ||
return new FutureClass(false, function Future$mapRej$fork(rej, res){ | ||
return _this._f(function Future$mapRej$rej(x){ | ||
rej(f(x)); | ||
}, res); | ||
}); | ||
let cancel; | ||
const r = _this._parent._f(function FutureChainRej$fork$rej(x){ | ||
const m = _this._chainer(x); | ||
check$chainRej$f(m, _this._chainer, x); | ||
cancel = m._f(rej, res); | ||
}, res); | ||
return cancel || (cancel = r, function FutureChainRej$fork$cancel(){ cancel() }); | ||
} | ||
function Future$bimap(f, g){ | ||
check$bimap(this, f, g); | ||
const _this = this; | ||
return new FutureClass(false, function Future$bimap$fork(rej, res){ | ||
return _this._f(function Future$bimap$rej(x){ | ||
rej(f(x)); | ||
}, function Future$bimap$res(x){ | ||
res(g(x)); | ||
}); | ||
}); | ||
FutureChainRej.prototype.toString = function FutureChainRej$toString(){ | ||
return `${this._parent.toString()}.chainRej(${showf(this._chainer)})`; | ||
} | ||
function Future$ap(m){ | ||
check$ap(this, m); | ||
const _this = this; | ||
return new FutureClass(false, function Future$ap$fork(g, res){ | ||
let _f, _x, ok1, ok2, ko; | ||
const rej = x => ko || (ko = 1, g(x)); | ||
const c1 = _this._f(rej, function Future$ap$resThis(x){ | ||
if(!ok1) return void (ok2 = 1, _x = x) | ||
check$ap$f(_f); | ||
res(_f(x)); | ||
}); | ||
const c2 = m._f(rej, function Future$ap$resThat(f){ | ||
if(!ok2) return void (ok1 = 1, _f = f); | ||
check$ap$f(f); | ||
res(f(_x)); | ||
}); | ||
return function Future$ap$cancel(){ c1(); c2() }; | ||
}); | ||
//---------- | ||
function FutureMap(parent, mapper){ | ||
this._parent = parent; | ||
this._mapper = mapper; | ||
} | ||
function Future$swap(){ | ||
check$swap(this); | ||
FutureMap.prototype = Object.create(Future.prototype); | ||
FutureMap.prototype._f = function FutureMap$fork(rej, res){ | ||
const _this = this; | ||
return new FutureClass(false, function Future$swap$fork(rej, res){ | ||
return _this._f(res, rej); | ||
return _this._parent._f(rej, function FutureMap$fork$res(x){ | ||
res(_this._mapper(x)); | ||
}); | ||
} | ||
function Future$toString(){ | ||
return `Future(${this._raw.toString()})`; | ||
FutureMap.prototype.toString = function FutureMap$toString(){ | ||
return `${this._parent.toString()}.map(${showf(this._mapper)})`; | ||
} | ||
function Future$race(m){ | ||
check$race(this, m); | ||
const _this = this; | ||
return new FutureClass(false, function Future$race$fork(rej, res){ | ||
let settled = false, c1 = noop, c2 = noop; | ||
const once = f => a => settled || (settled = true, c1(), c2(), f(a)); | ||
c1 = _this._f(once(rej), once(res)); | ||
c2 = m._f(once(rej), once(res)); | ||
return function Future$race$cancel(){ c1(); c2() }; | ||
}); | ||
//---------- | ||
function FutureMapRej(parent, mapper){ | ||
this._parent = parent; | ||
this._mapper = mapper; | ||
} | ||
function Future$or(m){ | ||
check$or(this, m); | ||
FutureMapRej.prototype = Object.create(Future.prototype); | ||
FutureMapRej.prototype._f = function FutureMapRej$fork(rej, res){ | ||
const _this = this; | ||
return new FutureClass(false, function Future$or$fork(rej, res){ | ||
let ok = false, ko = false, val, err; | ||
const c1 = _this._f( | ||
() => ko ? rej(err) : ok ? res(val) : (ko = true), | ||
x => (ok = true, res(x)) | ||
); | ||
const c2 = m._f( | ||
e => ok || (ko ? rej(e) : (err = e, ko = true)), | ||
x => ok || (ko ? res(x) : (val = x, ok = true)) | ||
); | ||
return function Future$or$cancel(){ c1(); c2() }; | ||
}); | ||
return _this._parent._f(function FutureMapRej$fork$rej(x){ | ||
rej(_this._mapper(x)); | ||
}, res); | ||
} | ||
function Future$fold(f, g){ | ||
check$fold(this, f, g); | ||
const _this = this; | ||
return new FutureClass(false, function Future$fold$fork(rej, res){ | ||
return _this._f(e => res(f(e)), x => res(g(x))); | ||
}); | ||
FutureMapRej.prototype.toString = function FutureMapRej$toString(){ | ||
return `${this._parent.toString()}.mapRej(${showf(this._mapper)})`; | ||
} | ||
function Future$hook(dispose, consume){ | ||
check$hook(this, dispose, consume); | ||
const _this = this; | ||
return new FutureClass(false, function Future$hook$fork(rej, res){ | ||
let cancel; | ||
const ret = _this._f(rej, function Future$hook$res(resource){ | ||
const m = consume(resource); | ||
check$hook$g(m, consume, resource); | ||
cancel = m._f(e => { | ||
const c = dispose(resource); | ||
check$hook$f(c, dispose, resource); | ||
c._f(rej, _ => rej(e)); | ||
}, x => { | ||
const c = dispose(resource); | ||
check$hook$f(c, dispose, resource); | ||
c._f(rej, _ => res(x)); | ||
}); | ||
}); | ||
cancel = cancel || ret; | ||
return function Future$hook$cancel(){ cancel() }; | ||
}); | ||
//---------- | ||
function FutureBimap(parent, lmapper, rmapper){ | ||
this._parent = parent; | ||
this._lmapper = lmapper; | ||
this._rmapper = rmapper; | ||
} | ||
function Future$finally(m){ | ||
check$finally(this, m); | ||
FutureBimap.prototype = Object.create(Future.prototype); | ||
FutureBimap.prototype._f = function FutureBimap$fork(rej, res){ | ||
const _this = this; | ||
return new FutureClass(false, function Future$finally$fork(rej, res){ | ||
let cancel; | ||
const r = _this._f(function Future$finally$rej(e){ | ||
cancel = m._f(rej, function Future$finally$rej$res(){ rej(e) }); | ||
}, function Future$finally$res(x){ | ||
cancel = m._f(rej, function Future$finally$res$res(){ res(x) }); | ||
}); | ||
return cancel ? cancel : (cancel = r, function Future$finally$cancel(){ cancel() }); | ||
return _this._parent._f(function FutureBimap$fork$rej(x){ | ||
rej(_this._lmapper(x)); | ||
}, function FutureBimap$fork$res(x){ | ||
res(_this._rmapper(x)); | ||
}); | ||
} | ||
function Future$value(f){ | ||
check$value(this, f); | ||
return this._f( | ||
function Future$value$rej(e){ | ||
throw new Error( | ||
`Future#value was called on a rejected Future\n Actual: Future.reject(${show(e)})` | ||
); | ||
}, | ||
f | ||
FutureBimap.prototype.toString = function FutureBimap$toString(){ | ||
return `${this._parent.toString()}.bimap(${showf(this._lmapper)}, ${showf(this._rmapper)})`; | ||
} | ||
//---------- | ||
function check$ap$f(f){ | ||
if(!isFunction(f)) throw new TypeError( | ||
'Future#ap expects its first argument to be a Future of a Function' | ||
+ `\n Actual: Future.of(${show(f)})` | ||
); | ||
} | ||
function Future$promise(){ | ||
check$promise(this); | ||
const _this = this; | ||
return new Promise(function Future$promise$do(resolve, reject){ | ||
_this._f(reject, resolve); | ||
}); | ||
function FutureAp(mval, mfunc){ | ||
this._mval = mval; | ||
this._mfunc = mfunc; | ||
} | ||
function Future$cache(){ | ||
check$cache(this); | ||
FutureAp.prototype = Object.create(Future.prototype); | ||
FutureAp.prototype._f = function FutureAp$fork(rej, res){ | ||
const _this = this; | ||
let que = []; | ||
let value, state; | ||
const settleWith = newState => function Future$cache$settle(newValue){ | ||
value = newValue; state = newState; | ||
for(let i = 0, l = que.length; i < l; i++){ | ||
que[i][state](value); | ||
que[i] = undefined; | ||
} | ||
que = undefined; | ||
}; | ||
return new FutureClass(false, function Future$cache$fork(rej, res){ | ||
let cancel = noop; | ||
switch(state){ | ||
case 1: que.push({2: rej, 3: res}); break; | ||
case 2: rej(value); break; | ||
case 3: res(value); break; | ||
default: | ||
state = 1; | ||
que.push({2: rej, 3: res}); | ||
cancel = _this._f(settleWith(2), settleWith(3)); | ||
} | ||
return function Future$cache$cancel(){ | ||
que = []; | ||
value = undefined; | ||
state = undefined; | ||
cancel(); | ||
}; | ||
let cancel; | ||
const r = _this._mval._f(rej, function FutureAp$fork$res$x(x){ | ||
cancel = _this._mfunc._f(rej, function FutureAp$fork$res$f(f){ | ||
check$ap$f(f); | ||
cancel = noop; | ||
res(f(x)); | ||
}); | ||
}); | ||
return cancel || (cancel = r, function FutureAp$fork$cancel(){ cancel() }); | ||
} | ||
FutureClass.prototype = Future.prototype = { | ||
'@@type': TYPEOF_FUTURE, | ||
_f: null, | ||
fork: Future$fork, | ||
[FL.of]: Future$of, | ||
of: Future$of, | ||
[FL.chainRec]: Future$chainRec, | ||
[FL.chain]: Future$chain, | ||
chain: Future$chain, | ||
recur: Future$recur, | ||
chainRej: Future$chainRej, | ||
[FL.map]: Future$map, | ||
map: Future$map, | ||
mapRej: Future$mapRej, | ||
[FL.bimap]: Future$bimap, | ||
bimap: Future$bimap, | ||
[FL.ap]: Future$ap, | ||
ap: Future$ap, | ||
swap: Future$swap, | ||
toString: Future$toString, | ||
inspect: Future$toString, | ||
race: Future$race, | ||
or: Future$or, | ||
fold: Future$fold, | ||
hook: Future$hook, | ||
finally: Future$finally, | ||
value: Future$value, | ||
promise: Future$promise, | ||
cache: Future$cache | ||
}; | ||
FutureAp.prototype.toString = function FutureAp$toString(){ | ||
return `${this._mval.toString()}.ap(${this._mfunc.toString()})`; | ||
} | ||
Future[FL.of] = Future.of = Future$of; | ||
Future[FL.chainRec] = Future.chainRec = Future$chainRec; | ||
Future.Future = Future; | ||
//---------- | ||
Future.util = { | ||
Next, | ||
Done, | ||
isForkable, | ||
isFuture, | ||
isFunction, | ||
isBinary, | ||
isTernary, | ||
isPositiveInteger, | ||
isObject, | ||
isIterator, | ||
isIteration, | ||
preview, | ||
show, | ||
padf, | ||
showf, | ||
fid, | ||
unaryPartial, | ||
binaryPartial, | ||
ternaryPartial | ||
}; | ||
function FutureSwap(parent){ | ||
this._parent = parent; | ||
} | ||
///////////////// | ||
// Dispatchers // | ||
///////////////// | ||
FutureSwap.prototype = Object.create(Future.prototype); | ||
//Creates a dispatcher for a nullary method. | ||
function createNullaryDispatcher(method){ | ||
return function nullaryDispatch(m){ | ||
if(m && typeof m[method] === 'function') return m[method](); | ||
error$invalidArgument(`Future.${method}`, 1, `have a "${method}" method`, m); | ||
}; | ||
FutureSwap.prototype._f = function FutureSwap$fork(rej, res){ | ||
return this._parent._f(res, rej); | ||
} | ||
//Creates a dispatcher for a unary method. | ||
function createUnaryDispatcher(method){ | ||
return function unaryDispatch(a, m){ | ||
if(arguments.length === 1) return unaryPartial(unaryDispatch, a); | ||
if(m && typeof m[method] === 'function') return m[method](a); | ||
error$invalidArgument(`Future.${method}`, 1, `have a "${method}" method`, m); | ||
}; | ||
FutureSwap.prototype.toString = function FutureSwap$toString(){ | ||
return `${this._parent.toString()}.swap()`; | ||
} | ||
//Creates a dispatcher for a binary method. | ||
function createBinaryDispatcher(method){ | ||
return function binaryDispatch(a, b, m){ | ||
if(arguments.length === 1) return unaryPartial(binaryDispatch, a); | ||
if(arguments.length === 2) return binaryPartial(binaryDispatch, a, b); | ||
if(m && typeof m[method] === 'function') return m[method](a, b); | ||
error$invalidArgument(`Future.${method}`, 2, `have a "${method}" method`, m); | ||
}; | ||
//---------- | ||
function FutureRace(left, right){ | ||
this._left = left; | ||
this._right = right; | ||
} | ||
//Creates a dispatcher for a binary method, but takes the object first rather than last. | ||
function createInvertedBinaryDispatcher(method){ | ||
return function invertedBinaryDispatch(m, a, b){ | ||
if(arguments.length === 1) return unaryPartial(invertedBinaryDispatch, m); | ||
if(arguments.length === 2) return binaryPartial(invertedBinaryDispatch, m, a); | ||
if(m && typeof m[method] === 'function') return m[method](a, b); | ||
error$invalidArgument(`Future.${method}`, 0, `have a "${method}" method`, m); | ||
}; | ||
FutureRace.prototype = Object.create(Future.prototype); | ||
FutureRace.prototype._f = function FutureRace$fork(rej, res){ | ||
let cancelled = false, lcancel = noop, rcancel = noop; | ||
const cancel = function FutureRace$fork$cancel(){ cancelled = true; lcancel(); rcancel() }; | ||
const reject = function FutureRace$fork$rej(x){ cancel(); rej(x) } | ||
const resolve = function FutureRace$fork$res(x){ cancel(); res(x) } | ||
lcancel = this._left._f(reject, resolve); | ||
cancelled || (rcancel = this._right._f(reject, resolve)); | ||
return cancel; | ||
} | ||
Future.chain = createUnaryDispatcher(FL.chain); | ||
Future.recur = createUnaryDispatcher('recur'); | ||
Future.chainRej = createUnaryDispatcher('chainRej'); | ||
Future.map = createUnaryDispatcher(FL.map); | ||
Future.mapRej = createUnaryDispatcher('mapRej'); | ||
Future.bimap = createBinaryDispatcher(FL.bimap); | ||
Future.ap = createUnaryDispatcher(FL.ap); | ||
Future.swap = createNullaryDispatcher('swap'); | ||
Future.fork = createBinaryDispatcher('fork'); | ||
Future.race = createUnaryDispatcher('race'); | ||
Future.or = createUnaryDispatcher('or'); | ||
Future.fold = createBinaryDispatcher('fold'); | ||
Future.hook = createInvertedBinaryDispatcher('hook'); | ||
Future.finally = createUnaryDispatcher('finally'); | ||
Future.value = createUnaryDispatcher('value'); | ||
Future.promise = createNullaryDispatcher('promise'); | ||
Future.cache = createNullaryDispatcher('cache'); | ||
FutureRace.prototype.toString = function FutureRace$toString(){ | ||
return `${this._left.toString()}.race(${this._right.toString()})`; | ||
} | ||
///////////////////// | ||
// Other functions // | ||
///////////////////// | ||
//---------- | ||
Future.isFuture = isFuture; | ||
Future.isForkable = isForkable; | ||
function FutureAnd(left, right){ | ||
this._left = left; | ||
this._right = right; | ||
} | ||
Future.reject = function Future$reject(x){ | ||
return new FutureClass(false, function Future$reject$fork(rej){ | ||
rej(x); | ||
return noop; | ||
}); | ||
}; | ||
FutureAnd.prototype = Object.create(Future.prototype); | ||
Future.after = function Future$after(n, x){ | ||
if(arguments.length === 1) return unaryPartial(Future.after, n); | ||
check$after(n); | ||
return new FutureClass(false, function Future$after$fork(rej, res){ | ||
const t = setTimeout(res, n, x); | ||
return function Future$after$cancel(){ clearTimeout(t) }; | ||
}); | ||
}; | ||
FutureAnd.prototype._f = function FutureAnd$fork(rej, res){ | ||
let rejected = false, resolved = false, val, lcancel = noop, rcancel = noop; | ||
lcancel = this._left._f( | ||
e => (rejected = true, rcancel(), rej(e)), | ||
_ => rejected ? rej(val) : resolved ? res(val) : (resolved = true) | ||
); | ||
rcancel = this._right._f( | ||
e => resolved ? rej(e) : (rejected = true, val = e), | ||
x => resolved ? res(x) : (resolved = true, val = x) | ||
); | ||
return function FutureAnd$fork$cancel(){ lcancel(); rcancel() }; | ||
} | ||
Future.cast = function Future$cast(m){ | ||
check$cast(m); | ||
return new FutureClass(true, function Future$cast$fork(rej, res){ | ||
m.fork(rej, res); | ||
}); | ||
}; | ||
FutureAnd.prototype.toString = function FutureAnd$toString(){ | ||
return `${this._left.toString()}.and(${this._right.toString()})`; | ||
} | ||
Future.encase = function Future$encase(f, x){ | ||
check$encase(f); | ||
if(arguments.length === 1) return unaryPartial(Future.encase, f); | ||
return new FutureClass(false, function Future$encase$fork(rej, res){ | ||
let r; | ||
try{ r = f(x) }catch(e){ return void rej(e) } | ||
res(r); | ||
return noop; | ||
}); | ||
}; | ||
//---------- | ||
Future.encase2 = function Future$encase2(f, x, y){ | ||
check$encase2(f); | ||
if(arguments.length === 1) return unaryPartial(Future.encase2, f); | ||
if(arguments.length === 2) return binaryPartial(Future.encase2, f, x); | ||
return new FutureClass(false, function Future$encase2$fork(rej, res){ | ||
let r; | ||
try{ r = f(x, y) }catch(e){ return void rej(e) } | ||
res(r); | ||
return noop; | ||
function FutureOr(left, right){ | ||
this._left = left; | ||
this._right = right; | ||
} | ||
FutureOr.prototype = Object.create(Future.prototype); | ||
FutureOr.prototype._f = function FutureOr$fork(rej, res){ | ||
let resolved = false, rejected = false, val, err, lcancel = noop, rcancel = noop; | ||
lcancel = this._left._f( | ||
_ => rejected ? rej(err) : resolved ? res(val) : (rejected = true), | ||
x => (resolved = true, rcancel(), res(x)) | ||
); | ||
resolved || (rcancel = this._right._f( | ||
e => resolved || (rejected ? rej(e) : (err = e, rejected = true)), | ||
x => resolved || (rejected ? res(x) : (val = x, resolved = true)) | ||
)); | ||
return function FutureOr$fork$cancel(){ lcancel(); rcancel() }; | ||
} | ||
FutureOr.prototype.toString = function FutureOr$toString(){ | ||
return `${this._left.toString()}.or(${this._right.toString()})`; | ||
} | ||
//---------- | ||
function FutureBoth(left, right){ | ||
this._left = left; | ||
this._right = right; | ||
} | ||
FutureBoth.prototype = Object.create(Future.prototype); | ||
FutureBoth.prototype._f = function FutureBoth$fork(rej, res){ | ||
let resolved = false, rejected = false, lcancel = noop, rcancel = noop; | ||
const tuple = new Array(2); | ||
lcancel = this._left._f(function FutureBoth$fork$rejLeft(e){ | ||
rejected = true; rcancel(); rej(e); | ||
}, function FutureBoth$fork$resLeft(x){ | ||
tuple[0] = x; | ||
if(resolved) res(tuple); | ||
else (resolved = true); | ||
}); | ||
}; | ||
rejected || (rcancel = this._right._f(function FutureBoth$fork$rejRight(e){ | ||
rejected = true; lcancel(); rej(e); | ||
}, function FutureBoth$fork$resRight(x){ | ||
tuple[1] = x; | ||
if(resolved) res(tuple); | ||
else (resolved = true); | ||
})); | ||
return function FutureBoth$fork$cancel(){ lcancel(); rcancel() }; | ||
} | ||
Future.encase3 = function Future$encase3(f, x, y, z){ | ||
check$encase3(f); | ||
if(arguments.length === 1) return unaryPartial(Future.encase3, f); | ||
if(arguments.length === 2) return binaryPartial(Future.encase3, f, x); | ||
if(arguments.length === 3) return ternaryPartial(Future.encase3, f, x, y); | ||
return new FutureClass(false, function Future$encase3$fork(rej, res){ | ||
let r; | ||
try{ r = f(x, y, z) }catch(e){ return void rej(e) } | ||
res(r); | ||
return noop; | ||
FutureBoth.prototype.toString = function FutureBoth$toString(){ | ||
return `${this._left.toString()}.both(${this._right.toString()})`; | ||
} | ||
//---------- | ||
function FutureFold(parent, lfold, rfold){ | ||
this._parent = parent; | ||
this._lfold = lfold; | ||
this._rfold = rfold; | ||
} | ||
FutureFold.prototype = Object.create(Future.prototype); | ||
FutureFold.prototype._f = function FutureFold$fork(rej, res){ | ||
const _this = this; | ||
return _this._parent._f(function FutureFold$fork$rej(x){ | ||
res(_this._lfold(x)); | ||
}, function FutureFold$fork$res(x){ | ||
res(_this._rfold(x)); | ||
}); | ||
}; | ||
} | ||
Future.try = function Future$try(f){ | ||
return Future.encase(f, undefined); | ||
}; | ||
FutureFold.prototype.toString = function FutureFold$toString(){ | ||
return `${this._parent.toString()}.fold(${showf(this._lfold)}, ${showf(this._rfold)})`; | ||
} | ||
Future.node = function Future$node(f){ | ||
check$node(f); | ||
return new FutureClass(true, function Future$node$fork(rej, res){ | ||
f(function Future$node$done(a, b){ | ||
a ? rej(a) : res(b); | ||
}); | ||
//---------- | ||
function check$hook$f(m, f, x){ | ||
if(!isFuture(m)) throw new TypeError( | ||
'Future#hook expects the first function its given to return a Future' | ||
+ `\n Actual: ${show(m)}\n From calling: ${showf(f)}\n With: ${show(x)}` | ||
); | ||
} | ||
function check$hook$g(m, g, x){ | ||
if(!isFuture(m)) throw new TypeError( | ||
'Future#hook expects the second function its given to return a Future' | ||
+ `\n Actual: ${show(m)}\n From calling: ${showf(g)}\n With: ${show(x)}` | ||
); | ||
} | ||
function FutureHook(acquire, dispose, consume){ | ||
this._acquire = acquire; | ||
this._dispose = dispose; | ||
this._consume = consume; | ||
} | ||
FutureHook.prototype = Object.create(Future.prototype); | ||
FutureHook.prototype._f = function FutureHook$fork(rej, res){ | ||
const _this = this; | ||
let cancel, cancelAcquire = noop; | ||
cancelAcquire = _this._acquire._f(rej, function FutureHook$fork$res(resource){ | ||
const disposer = function FutureHook$dispose(callback){ | ||
const disposal = _this._dispose(resource); | ||
check$hook$f(disposal, _this._dispose, resource); | ||
cancel = disposal._f(rej, callback); | ||
return cancel; | ||
} | ||
const consumption = _this._consume(resource); | ||
check$hook$g(consumption, _this._consume, resource); | ||
cancel = function FutureHook$fork$cancelConsume(){ | ||
disposer(noop)(); | ||
cancelAcquire(); | ||
cancelConsume(); | ||
} | ||
const cancelConsume = consumption._f( | ||
x => disposer(_ => rej(x)), | ||
x => disposer(_ => res(x)) | ||
); | ||
}); | ||
}; | ||
cancel = cancel || cancelAcquire; | ||
return function FutureHook$fork$cancel(){ cancel() }; | ||
} | ||
Future.parallel = function Future$parallel(i, ms){ | ||
if(arguments.length === 1) return unaryPartial(Future.parallel, i); | ||
check$parallel(i, ms); | ||
const l = ms.length; | ||
return l < 1 ? Future$of([]) : new FutureClass(false, function Future$parallel$fork(rej, res){ | ||
let ko = false; | ||
let ok = 0; | ||
const cs = []; | ||
const out = new Array(l); | ||
const next = j => i < l ? fork(ms[i], i++) : (j === l && res(out)); | ||
const fork = (m, j) => (check$parallel$m(m, j), cs[j] = m._f( | ||
e => ko || (rej(e), ko = true), | ||
x => ko || (out[j] = x, next(++ok)) | ||
)); | ||
ms.slice(0, i).forEach(fork); | ||
return function Future$parallel$cancel(){ cs.forEach(call) }; | ||
FutureHook.prototype.toString = function FutureHook$toString(){ | ||
return `${this._acquire.toString()}.hook(${showf(this._dispose)}, ${showf(this._consume)})`; | ||
} | ||
//---------- | ||
function FutureFinally(left, right){ | ||
this._left = left; | ||
this._right = right; | ||
} | ||
FutureFinally.prototype = Object.create(Future.prototype); | ||
FutureFinally.prototype._f = function FutureFinally$fork(rej, res){ | ||
const _this = this; | ||
let cancel; | ||
const r = _this._left._f(function FutureFinally$fork$rej(e){ | ||
cancel = _this._right._f(rej, function FutureFinally$fork$rej$res(){ rej(e) }); | ||
}, function FutureFinally$fork$res(x){ | ||
cancel = _this._right._f(rej, function FutureFinally$fork$res$res(){ res(x) }); | ||
}); | ||
}; | ||
return cancel || (cancel = r, function FutureFinally$fork$cancel(){ cancel() }); | ||
} | ||
Future.do = function Future$do(f){ | ||
check$do(f); | ||
return new FutureClass(false, function Future$do$fork(rej, res){ | ||
const g = f(); | ||
check$do$g(g); | ||
return Future$chainRec(function Future$do$next(next, _, x){ | ||
const o = g.next(x); | ||
check$do$next(o); | ||
return o.done ? Future$of(o) : o.value.map(next); | ||
}, undefined)._f(rej, res); | ||
}); | ||
FutureFinally.prototype.toString = function FutureFinally$toString(){ | ||
return `${this._left.toString()}.finally(${this._right.toString()})`; | ||
} | ||
Future.subclasses = { | ||
SafeFuture, | ||
ChainRec, | ||
CachedFuture, | ||
FutureOf, | ||
FutureReject, | ||
FutureNode, | ||
FutureAfter, | ||
FutureParallel, | ||
FutureDo, | ||
FutureCast, | ||
FutureTry, | ||
FutureEncase, | ||
FutureChain, | ||
FutureChainRej, | ||
FutureMap, | ||
FutureMapRej, | ||
FutureBimap, | ||
FutureAp, | ||
FutureSwap, | ||
FutureRace, | ||
FutureAnd, | ||
FutureOr, | ||
FutureBoth, | ||
FutureFold, | ||
FutureHook, | ||
FutureFinally | ||
}; | ||
@@ -927,0 +1442,0 @@ |
{ | ||
"name": "fluture", | ||
"version": "3.1.1", | ||
"version": "4.0.0", | ||
"description": "FantasyLand compliant (monadic) alternative to Promises", | ||
@@ -12,10 +12,12 @@ "main": "fluture.js", | ||
"post-merge": "npm install && npm dedupe && npm run check-security && npm outdated --long", | ||
"pre-push": "npm test", | ||
"pre-push": "npm run lint && npm run test:unit", | ||
"release": "xyz --edit --tag 'X.Y.Z' --increment", | ||
"setup": "npm run post-merge && cp scripts/hooks/* .git/hooks && git config push.followTags true", | ||
"toc": "node scripts/toc.js", | ||
"test": "npm run clean && npm run lint && npm run test:unit && npm run test:coverage", | ||
"test": "npm run test:all && codecov", | ||
"test:opt": "node --allow-natives-syntax --trace-opt --trace-deopt --trace-inlining scripts/test-opt", | ||
"test:mem": "node scripts/test-mem", | ||
"test:all": "npm run lint && npm run test:unit && npm run test:coverage", | ||
"test:unit": "node ./node_modules/.bin/_mocha --ui bdd --reporter spec --check-leaks --full-trace", | ||
"test:coverage": "node node_modules/.bin/istanbul cover --report html ./node_modules/.bin/_mocha -- --ui bdd --reporter dot --bail --check-leaks && codecov" | ||
"test:coverage": "npm run clean && node node_modules/.bin/istanbul cover --report html ./node_modules/.bin/_mocha -- --ui bdd --reporter dot --bail" | ||
}, | ||
@@ -54,3 +56,4 @@ "author": "Aldwin Vlasblom <aldwin.vlasblom@gmail.com> (https://github.com/Avaq)", | ||
"dependencies": { | ||
"inspect-f": "^1.1.0" | ||
"inspect-f": "^1.1.0", | ||
"sanctuary-type-classes": "^0.3.0" | ||
}, | ||
@@ -63,3 +66,3 @@ "devDependencies": { | ||
"eslint": "^3.0.1", | ||
"fantasy-land": "^1.0.1", | ||
"fantasy-land": "^2.0.0", | ||
"fun-task": "^1.5.1", | ||
@@ -69,3 +72,4 @@ "istanbul": "^0.4.2", | ||
"lazy-either": "^1.0.3", | ||
"markdown-toc": "^0.12.6", | ||
"lodash.curry": "^4.1.1", | ||
"markdown-toc": "^0.13.0", | ||
"mocha": "^3.0.2", | ||
@@ -77,4 +81,5 @@ "nsp": "^2.2.0", | ||
"rimraf": "^2.4.3", | ||
"sanctuary": "^0.11.1" | ||
"sanctuary": "^0.11.1", | ||
"xyz": "^1.1.0" | ||
} | ||
} |
165
README.md
@@ -70,3 +70,2 @@ # Fluture | ||
* [chain](#chain) | ||
* [recur](#recur) | ||
* [ap](#ap) | ||
@@ -87,3 +86,5 @@ * [swap](#swap) | ||
* [race](#race) | ||
* [and](#and) | ||
* [or](#or) | ||
* [both](#both) | ||
* [parallel](#parallel) | ||
@@ -95,2 +96,3 @@ 1. [Utility functions](#utility-functions) | ||
* [do](#do) | ||
1. [Sanctuary](#sanctuary) | ||
1. [Futurization](#futurization) | ||
@@ -122,6 +124,10 @@ - [Benchmarks](#benchmarks) | ||
- **Future** - Instances of Future provided by Fluture. | ||
- **Functor** - Values which conform to the [Fantasy Land Functor specification][12]. | ||
- **Bifunctor** - Values which conform to the [Fantasy Land Bifunctor specification][24]. | ||
- **Chain** - Values which conform to the [Fantasy Land Chain specification][13]. | ||
- **Apply** - Values which conform to the [Fantasy Land Apply specification][14]. | ||
- **Functor** - Values which conform to the [Fantasy Land Functor specification][12] | ||
as determined by [Sanctuary Type Classes][27]. | ||
- **Bifunctor** - Values which conform to the [Fantasy Land Bifunctor specification][24] | ||
as determined by [Sanctuary Type Classes][28]. | ||
- **Chain** - Values which conform to the [Fantasy Land Chain specification][13] | ||
as determined by [Sanctuary Type Classes][29]. | ||
- **Apply** - Values which conform to the [Fantasy Land Apply specification][14] | ||
as determined by [Sanctuary Type Classes][30]. | ||
- **Iterator** - Objects with `next`-methods which conform to the [Iterator protocol][18]. | ||
@@ -158,8 +164,6 @@ - **Iteration** - `{done, value}`-Objects as defined by the [Iterator protocol][18]. | ||
#### of | ||
##### `#of :: a -> Future _ a` | ||
##### `.of :: a -> Future _ a` | ||
Creates a Future which immediately resolves with the given value. This function | ||
is compliant with the [Fantasy Land Applicative specification][16] and is | ||
also available on the prototype. | ||
is compliant with the [Fantasy Land Applicative specification][16]. | ||
@@ -192,2 +196,13 @@ ```js | ||
#### rejectAfter | ||
##### `.rejectAfter :: Number -> a -> Future a b` | ||
Creates a Future which rejects with the given reason after n milliseconds. | ||
```js | ||
const eventualError = Future.rejectAfter(500, new Error('Kaputt!')); | ||
eventualError.fork(err => console.log('Oh no - ' + err.message), console.log); | ||
//! Oh no - Kaputt! | ||
``` | ||
#### cast | ||
@@ -340,16 +355,7 @@ ##### `.cast :: Forkable a b -> Future a b` | ||
#### recur | ||
##### `#recur :: Future a b ~> (b -> Future a c) -> Future a c` | ||
##### `.recur :: (b -> Future a c) -> Future a b -> Future a c` | ||
Note that, due to its lazy nature, the stack and/or heap will slowly fill up as | ||
you chain more over the same structure. It's therefore recommended that you use | ||
[`chainRec`](#chainrec) in cases where you wish to `chain` recursively or | ||
traverse a large list (10000+ items). | ||
An alternative version of `chain` which does not build up the cancel function. | ||
This is useful in the case of a never-resolving recursive computation, in | ||
order to prevent stack-overflow or out-of-memory errors: | ||
```js | ||
const recursive = () => Future.after(200, 'world').recur(recursive); | ||
const cancel = recursive(); | ||
process.on('SIGINT', cancel); | ||
``` | ||
#### ap | ||
@@ -359,6 +365,6 @@ ##### `#ap :: Future a b ~> Future a (b -> c) -> Future a c` | ||
Applies the function contained in the right-hand Future to the value contained | ||
in the left-hand Future. Both Futures involved will run in parallel, and if one | ||
rejects the resulting Future will also be rejected. To learn more about the | ||
exact behaviour of `ap`, check out its [spec][14]. | ||
Applies the function contained in the right-hand Future or Apply to the value | ||
contained in the left-hand Future or Apply. If one of the Futures rejects the | ||
resulting Future will also be rejected. To learn more about the exact behaviour | ||
of `ap`, check out its [spec][14]. | ||
@@ -476,10 +482,27 @@ ```js | ||
Be careful when cancelling a hooked Future. If the resource was acquired but not | ||
yet consumed, it will no longer be disposed. One way to work around this is to | ||
have the `consume` computation return a cancel function which forcefully | ||
disposes of the resource. | ||
In the case that a hooked Future is *cancelled* after the resource was acquired, | ||
`dispose` will be executed and immediately cancelled. This means that rejections | ||
which may happen during this disposal are **silently ignored**. To ensure that | ||
resources are disposed during cancellation, you might synchronously dispose | ||
resources in the `cancel` function of the disposal Future: | ||
Take care when using this in combination with [`cache`](#cache). Hooking relies | ||
on the first operation providing a fresh resource every time it's forked. | ||
```js | ||
const closeConnection = conn => Future((rej, res) => { | ||
//We try to dispose gracefully. | ||
conn.flushGracefully(err => { | ||
if(err === null){ | ||
conn.close(); | ||
res(); | ||
}else{ | ||
rej(err); | ||
} | ||
}); | ||
//On cancel, we force dispose. | ||
return () => conn.close(); | ||
}); | ||
``` | ||
#### finally | ||
@@ -521,3 +544,3 @@ ##### `#finally :: Future a b ~> Future a c -> Future a b` | ||
Execute the computation that was passed to the Future at [construction](#Future) | ||
Execute the computation that was passed to the Future at [construction](#future) | ||
using the given `reject` and `resolve` callbacks. | ||
@@ -615,2 +638,22 @@ | ||
#### and | ||
##### `#and :: Future a b ~> Future a b -> Future a b` | ||
##### `.and :: Future a b -> Future a b -> Future a b` | ||
Logical and for Futures. | ||
Returns a new Future which either rejects with the first rejection reason, or | ||
resolves with the last resolution value once and if both Futures resolve. | ||
This behaves analogues to how JavaScript's and operator does, except both | ||
Futures run simultaneously, so it is *not* short-circuited. That means that | ||
if the second has side-effects, they will run (and possibly be cancelled) even | ||
if the first rejects. | ||
```js | ||
//An asynchronous version of: | ||
//const result = isResolved() && getValue(); | ||
const result = isResolved().and(getValue()); | ||
``` | ||
#### or | ||
@@ -648,2 +691,17 @@ ##### `#or :: Future a b ~> Future a b -> Future a b` | ||
#### both | ||
##### `#both :: Future a b ~> Future a b -> Future a b` | ||
##### `.both :: Future a b -> Future a b -> Future a b` | ||
Run two Futures in parallel. Basically like calling | ||
[`Future.parallel`](#parallel) with exactly two Futures: | ||
```js | ||
Future.parallel(2, [a, b]) | ||
=== | ||
Future.both(a, b) | ||
=== | ||
a.both(b) | ||
``` | ||
#### parallel | ||
@@ -789,2 +847,39 @@ ##### `.parallel :: PositiveInteger -> Array (Future a b) -> Future a (Array b)` | ||
### Sanctuary | ||
When using this module with [Sanctuary][21], you might run into the following: | ||
```js | ||
const S = require('sanctuary'); | ||
const Future = require('fluture'); | ||
S.I(Future.of(1)); | ||
//! Since there is no type of which all the above values are members, | ||
//! the type-variable constraint has been violated. | ||
``` | ||
This happens because Sanctuary needs to know about the Future type in order to | ||
determine whether the type-variable used in the definition of `S.I` is | ||
consistent. | ||
To let Sanctuary know about Futures, we can provide it a `FutureType` using | ||
[Sanctuary Def][31], and pass it to Sanctuary using [`S.create`][32] | ||
```js | ||
const $ = require('sanctuary-def'); | ||
const Future = require('fluture'); | ||
const {env, create} = require('sanctuary'); | ||
const FutureType = $.BinaryType( | ||
Future.name, | ||
Future.isFuture, | ||
Future.extractLeft, | ||
Future.extractRight | ||
); | ||
const S = create({checkTypes: true, env: env.concat([FutureType])}); | ||
S.I(Future.of(1)); | ||
//> Future.of(1) | ||
``` | ||
### Futurization | ||
@@ -851,1 +946,7 @@ | ||
[26]: https://github.com/fantasyland/fantasy-land#chainrec | ||
[27]: https://github.com/sanctuary-js/sanctuary-type-classes#Functor | ||
[28]: https://github.com/sanctuary-js/sanctuary-type-classes#Bifunctor | ||
[29]: https://github.com/sanctuary-js/sanctuary-type-classes#Chain | ||
[30]: https://github.com/sanctuary-js/sanctuary-type-classes#Apply | ||
[31]: https://github.com/sanctuary-js/sanctuary-def#binarytype | ||
[32]: https://sanctuary.js.org/#create |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
78092
1187
939
2
20
1
+ Addedsanctuary-type-classes@0.3.1(transitive)