fluture
Advanced tools
Comparing version 0.4.0 to 0.4.1
178
fluture.js
@@ -33,3 +33,3 @@ /*global define*/ | ||
? x.toString === Object.prototype.toString | ||
? `{${Object.keys(x).reduce((o, k) => o.concat(`"${k}": ${toString(x[k])}`), []).join(', ')}}` | ||
? `{${Object.keys(x).reduce((o, k) => o.concat(`${toString(k)}: ${toString(x[k])}`), []).join(', ')}}` | ||
: x.toString() | ||
@@ -106,2 +106,19 @@ : String(x); | ||
function check$cache(m){ | ||
if(!(m instanceof FutureClass)) throw new TypeError(error( | ||
'Future.cache expects its argument to be a Future', | ||
toString(m) | ||
)); | ||
} | ||
function check$cache$settle(oldState, newState, oldValue, newValue){ | ||
if(oldState > 1) throw new Error( | ||
'Future.cache expects the Future it wraps to only resolve or reject once; ' | ||
+ ' a cached Future tried to ' + (newState === 2 ? 'reject' : 'resolve') + ' a second time.' | ||
+ ' Please check your cached Future and make sure it does not call res or rej multiple times' | ||
+ '\n It was ' + (oldState === 2 ? 'rejected' : 'resolved') + ' with: ' + toString(oldValue) | ||
+ '\n It got ' + (newState === 2 ? 'rejected' : 'resolved') + ' with: ' + toString(newValue) | ||
); | ||
} | ||
function check$liftNode(f){ | ||
@@ -163,9 +180,2 @@ if(typeof f !== 'function') throw new TypeError(error( | ||
//The of method. | ||
function Future$of(x){ | ||
return new FutureClass(function Future$of$fork(rej, res){ | ||
res(x) | ||
}); | ||
} | ||
//Constructor. | ||
@@ -182,64 +192,77 @@ function FutureClass(f){ | ||
//Give Future a prototype. | ||
FutureClass.prototype = Future.prototype = { | ||
//The of method. | ||
function Future$of(x){ | ||
return new FutureClass(function Future$of$fork(rej, res){ | ||
res(x) | ||
}); | ||
} | ||
_f: null, | ||
function Future$fork(rej, res){ | ||
check$fork$rej(rej); | ||
check$fork$res(res); | ||
this._f(rej, res); | ||
} | ||
fork: function Future$fork(rej, res){ | ||
check$fork$rej(rej); | ||
check$fork$res(res); | ||
this._f(rej, res); | ||
}, | ||
[FL.of]: Future$of, | ||
[FL.chain]: function Future$chain(f){ | ||
check$chain(f); | ||
const _this = this; | ||
return new FutureClass(function Future$chain$fork(rej, res){ | ||
_this._f(rej, function Future$chain$res(x){ | ||
const m = f(x); | ||
check$chain$f(m, f, x); | ||
m._f(rej, res); | ||
}); | ||
function Future$chain(f){ | ||
check$chain(f); | ||
const _this = this; | ||
return new FutureClass(function Future$chain$fork(rej, res){ | ||
_this._f(rej, function Future$chain$res(x){ | ||
const m = f(x); | ||
check$chain$f(m, f, x); | ||
m._f(rej, res); | ||
}); | ||
}, | ||
}); | ||
} | ||
[FL.map]: function Future$map(f){ | ||
check$map(f); | ||
const _this = this; | ||
return new FutureClass(function Future$map$fork(rej, res){ | ||
_this._f(rej, function Future$map$res(x){ | ||
res(f(x)); | ||
}); | ||
function Future$map(f){ | ||
check$map(f); | ||
const _this = this; | ||
return new FutureClass(function Future$map$fork(rej, res){ | ||
_this._f(rej, function Future$map$res(x){ | ||
res(f(x)); | ||
}); | ||
}, | ||
}); | ||
} | ||
[FL.ap]: function Future$ap(m){ | ||
check$ap(m); | ||
const _this = this; | ||
return new FutureClass(function Future$ap$fork(g, h){ | ||
let _f, _x, ok1, ok2, ko; | ||
const rej = x => ko || (ko = 1, g(x)); | ||
_this._f(rej, function Future$ap$resThis(f){ | ||
if(!ok2) return void (ok1 = 1, _f = f); | ||
check$ap$f(f); | ||
h(f(_x)); | ||
}); | ||
m._f(rej, function Future$ap$resThat(x){ | ||
if(!ok1) return void (ok2 = 1, _x = x) | ||
check$ap$f(_f); | ||
h(_f(x)); | ||
}); | ||
function Future$ap(m){ | ||
check$ap(m); | ||
const _this = this; | ||
return new FutureClass(function Future$ap$fork(g, h){ | ||
let _f, _x, ok1, ok2, ko; | ||
const rej = x => ko || (ko = 1, g(x)); | ||
_this._f(rej, function Future$ap$resThis(f){ | ||
if(!ok2) return void (ok1 = 1, _f = f); | ||
check$ap$f(f); | ||
h(f(_x)); | ||
}); | ||
}, | ||
m._f(rej, function Future$ap$resThat(x){ | ||
if(!ok1) return void (ok2 = 1, _x = x) | ||
check$ap$f(_f); | ||
h(_f(x)); | ||
}); | ||
}); | ||
} | ||
toString: function Future$toString(){ | ||
return `Future(${toString(this._f)})`; | ||
} | ||
function Future$toString(){ | ||
return `Future(${toString(this._f)})`; | ||
} | ||
//Give Future a prototype. | ||
FutureClass.prototype = Future.prototype = { | ||
_f: null, | ||
fork: Future$fork, | ||
[FL.of]: Future$of, | ||
of: Future$of, | ||
[FL.chain]: Future$chain, | ||
chain: Future$chain, | ||
[FL.map]: Future$map, | ||
map: Future$map, | ||
[FL.ap]: Future$ap, | ||
ap: Future$ap, | ||
toString: Future$toString | ||
}; | ||
//Expose `of` statically as well. | ||
Future[FL.of] = Future[FL.of] = Future$of; | ||
Future[FL.of] = Future.of = Future$of; | ||
@@ -250,2 +273,39 @@ //Expose Future statically for ease of destructuring. | ||
/** | ||
* Cache a Future | ||
* | ||
* Returns a Future which caches the resolution value of the given Future so | ||
* that whenever it's forked, it can load the value from cache rather than | ||
* reexecuting the chain. | ||
* | ||
* @param {Future} m The Future to be cached. | ||
* | ||
* @return {Future} The Future which does the caching. | ||
*/ | ||
Future.cache = function Future$cache(m){ | ||
check$cache(m); | ||
let que = []; | ||
let value, state; | ||
const settleWith = newState => function Future$cache$settle(newValue){ | ||
check$cache$settle(state, newState, value, 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(function Future$cache$fork(rej, res){ | ||
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}); | ||
m.fork(settleWith(2), settleWith(3)); | ||
} | ||
}); | ||
}; | ||
/** | ||
* Turn a node continuation-passing-style function into a function which returns a Future. | ||
@@ -277,3 +337,3 @@ * | ||
* | ||
* @sig liftPromise :: (x... -> a) -> x... -> Future[Error, a] | ||
* @sig liftPromise :: (x... -> Promise a b) -> x... -> Future a b | ||
* | ||
@@ -280,0 +340,0 @@ * @param {Function} f The function to wrap. |
{ | ||
"name": "fluture", | ||
"version": "0.4.0", | ||
"version": "0.4.1", | ||
"description": "A complete Fantasy Land compatible Future library", | ||
@@ -16,2 +16,3 @@ "main": "fluture.js", | ||
"test": "npm run check-version && npm run clean && npm run lint && npm run test:unit", | ||
"test:opt": "node --allow-natives-syntax --trace-opt --trace-deopt --trace-inlining scripts/test-opt", | ||
"test:unit": "mocha --ui bdd --reporter spec --check-leaks --full-trace" | ||
@@ -18,0 +19,0 @@ }, |
@@ -5,3 +5,4 @@ # Fluture | ||
> `npm install --save fluture` <sup>Requires node 5.0.0 or later</sup> | ||
> `npm install --save fluture` <sup>Requires a node 5.0.0 compatible environment | ||
like modern browsers, transpilers or Node 5+</sup> | ||
@@ -90,2 +91,24 @@ | ||
#### `cache :: Future a b -> Future a b` | ||
Returns a Future which caches the resolution value of the given Future so that | ||
whenever it's forked, it can load the value from cache rather than reexecuting | ||
the chain. | ||
```js | ||
const eventualPackage = Future.cache( | ||
Future.node(done => { | ||
console.log('Reading some big data'); | ||
fs.readFile('package.json', 'utf8', done) | ||
}) | ||
); | ||
eventualPackage.fork(console.error, console.log); | ||
//> "Reading some big data" | ||
//> "{...}" | ||
eventualPackage.fork(console.error, console.log); | ||
//> "{...}" | ||
``` | ||
---- | ||
@@ -145,2 +168,26 @@ | ||
---- | ||
#### `liftNode :: (x..., (a, b -> Void) -> Void) -> x... -> Future a b` | ||
Turn a node continuation-passing-style function into a function which returns a Future. | ||
Takes a function which uses a node-style callback for continuation and returns a | ||
function which returns a Future for continuation. | ||
```js | ||
const readFile = Future.liftNode(fs.readFile); | ||
readFile('README.md', 'utf8') | ||
.map(text => text.split('\n')) | ||
.map(lines => lines[0]) | ||
.fork(console.error, console.log); | ||
//> "# Fluture" | ||
``` | ||
#### `liftPromise :: (x... -> Promise a b) -> x... -> Future a b` | ||
Turn a function which returns a Promise into a function which returns a Future. | ||
Like liftNode, but for a function which returns a Promise. | ||
## Road map | ||
@@ -152,5 +199,6 @@ | ||
* [ ] Implement Traversable? | ||
* [ ] Implement Future.cache | ||
* [x] Implement Future.cache | ||
* [ ] Implement Future.mapRej | ||
* [ ] Implement Future.chainRej | ||
* [ ] Implement dispatchers: chain, map, ap, fork | ||
* [ ] Implement Future.swap | ||
@@ -161,2 +209,5 @@ * [ ] Implement Future.and | ||
* [ ] Implement Future.parallel | ||
* [ ] Implement Future.predicate | ||
* [ ] Implement Future.promise | ||
* [ ] Implement Future.cast | ||
* [x] Create documentation | ||
@@ -180,1 +231,5 @@ * [ ] Wiki: Comparison between Future libs | ||
means butterfly in Romanian; A creature you might expect to see in Fantasy Land. | ||
---- | ||
[MIT licensed](LICENSE) |
Sorry, the diff of this file is not supported yet
20634
5
387
230