diff --git a/README.md b/README.md index a9f3ff30..166400e7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Fluture -[Fantasy Land][1] - [![NPM Version](https://badge.fury.io/js/fluture.svg)](https://www.npmjs.com/package/fluture) [![Dependencies](https://david-dm.org/avaq/fluture.svg)](https://david-dm.org/avaq/fluture) [![Build Status](https://travis-ci.org/Avaq/Fluture.svg?branch=master)](https://travis-ci.org/Avaq/Fluture) @@ -51,6 +49,7 @@ getPackageName('package.json') ## Table of contents - [Usage](#usage) +- [Interoperability](#interoperability) - [Documentation](#documentation) 1. [Type signatures](#type-signatures) 1. [Creating Futures](#creating-futures) @@ -93,6 +92,13 @@ getPackageName('package.json') - [Benchmarks](#benchmarks) - [The name](#the-name) +## Interoperability + +[Fantasy Land][1] + +Fluture implements [FantasyLand 1.x][1] compatible `Functor`, `Bifunctor`, +`Apply`, `Applicative`, `Chain` and `Monad`. + ## Documentation ### Type signatures @@ -314,18 +320,17 @@ process.on('SIGINT', cancel); ``` #### ap -##### `#ap :: Future a (b -> c) ~> Future a b -> Future a c` +##### `#ap :: Future a b ~> Future a (b -> c) -> Future a c` ##### `.ap :: Apply m => m (a -> b) -> m a -> m b` -Apply the resolution value, which is expected to be a function (as in -`Future.of(a_function)`), to the resolution value in the given 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 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]. ```js -Future.of(x => x + 1) -.ap(Future.of(1)) +Future.of(1) +.ap(Future.of(x => x + 1)) .fork(console.error, console.log); //> 2 ``` diff --git a/fluture.js b/fluture.js index 30522fa4..0f3c24b5 100644 --- a/fluture.js +++ b/fluture.js @@ -28,11 +28,11 @@ const TYPEOF_FUTURE = 'fluture/Future'; const FL = { - map: 'map', - bimap: 'bimap', - chain: 'chain', - ap: 'ap', - of: 'of' + map: 'fantasy-land/map', + bimap: 'fantasy-land/bimap', + chain: 'fantasy-land/chain', + ap: 'fantasy-land/ap', + of: 'fantasy-land/of' }; function isForkable(m){ @@ -222,7 +222,8 @@ function check$ap$f(f){ if(!isFunction(f)) throw new TypeError( - `Future#ap can only be used on Future but was used on a Future of: ${show(f)}` + 'Future#ap expects its first argument to be a Future of a Function' + + `\n Actual: Future.of(${show(f)})` ); } @@ -317,8 +318,8 @@ function check$parallel$m(m, i){ if(!isFuture(m)) throw new TypeError( - 'Future.parallel expects argument 1 to be an array of Futures.' - + ` The value at position ${i} in the array was not a Future.\n Actual: ${show(m)}` + 'Future.parallel expects its second argument to be an array of Futures.' + + ` The value at position ${i} in the array was not a Future\n Actual: ${show(m)}` ); } @@ -474,16 +475,16 @@ return new FutureClass(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(f){ - if(!ok2) return void (ok1 = 1, _f = f); - check$ap$f(f); - res(f(_x)); - }); - const c2 = m._f(rej, function Future$ap$resThat(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() }; }); } @@ -701,15 +702,6 @@ }; } - //Creates a dispatcher for a unary method, but takes the object first rather than last. - function createInvertedUnaryDispatcher(method){ - return function invertedUnaryDispatch(m, a){ - if(arguments.length === 1) return unaryPartial(invertedUnaryDispatch, m); - if(m && typeof m[method] === 'function') return m[method](a); - error$invalidArgument(`Future.${method}`, 0, `have a "${method}" method`, m); - }; - } - //Creates a dispatcher for a binary method. function createBinaryDispatcher(method){ return function binaryDispatch(a, b, m){ @@ -730,13 +722,13 @@ }; } - Future.chain = createUnaryDispatcher('chain'); + Future.chain = createUnaryDispatcher(FL.chain); Future.recur = createUnaryDispatcher('recur'); Future.chainRej = createUnaryDispatcher('chainRej'); - Future.map = createUnaryDispatcher('map'); + Future.map = createUnaryDispatcher(FL.map); Future.mapRej = createUnaryDispatcher('mapRej'); - Future.bimap = createBinaryDispatcher('bimap'); - Future.ap = createInvertedUnaryDispatcher('ap'); + Future.bimap = createBinaryDispatcher(FL.bimap); + Future.ap = createUnaryDispatcher(FL.ap); Future.swap = createNullaryDispatcher('swap'); Future.fork = createBinaryDispatcher('fork'); Future.race = createUnaryDispatcher('race'); diff --git a/package.json b/package.json index 0716e14d..14ee53fd 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "codecov": "^1.0.1", "data.task": "^3.0.0", "eslint": "^3.0.1", + "fantasy-land": "^1.0.1", "fun-task": "^1.1.1", "istanbul": "^0.4.2", "jsverify": "^0.7.1", diff --git a/test/fluture.test.js b/test/fluture.test.js index 5eccaf82..f29ec99e 100644 --- a/test/fluture.test.js +++ b/test/fluture.test.js @@ -5,13 +5,7 @@ const Future = require('../fluture'); const Readable = require('stream').Readable; const jsc = require('jsverify'); const S = require('sanctuary'); -const FL = { - map: 'map', - bimap: 'bimap', - chain: 'chain', - ap: 'ap', - of: 'of' -}; +const FL = require('fantasy-land'); const noop = () => {}; const add = a => b => a + b; @@ -726,14 +720,14 @@ describe('Future', () => { fs.forEach(f => expect(f).to.throw(TypeError, /Future/)); }); - it('throws TypeError when not not called on Future', () => { + it('throws TypeError when not not called with Future', () => { const xs = [NaN, {}, [], 1, 'a', new Date, undefined, null]; - const fs = xs.map(x => () => Future.of(x).ap(Future.of(1)).fork(noop, noop)); + const fs = xs.map(x => () => Future.of(1).ap(Future.of(x)).fork(noop, noop)); fs.forEach(f => expect(f).to.throw(TypeError, /Future/)); }); - it('applies its inner to the inner of the other', () => { - const actual = Future.of(add(1)).ap(Future.of(1)); + it('calls the function contained in the given Future to its contained value', () => { + const actual = Future.of(1).ap(Future.of(add(1))); return assertResolved(actual, 2); }); @@ -747,19 +741,19 @@ describe('Future', () => { }); it('does not matter if the left resolves late', () => { - const actual = Future.after(20, add(1)).ap(Future.of(1)); + const actual = Future.after(20, 1).ap(Future.of(add(1))); return assertResolved(actual, 2); }); it('does not matter if the right resolves late', () => { - const actual = Future.of(add(1)).ap(Future.after(20, 1)); + const actual = Future.of(1).ap(Future.after(20, add(1))); return assertResolved(actual, 2); }); it('forks in parallel', function(){ this.slow(80); this.timeout(50); - const actual = Future.after(30, add(1)).ap(Future.after(30, 1)); + const actual = Future.after(30, 1).ap(Future.after(30, add(1))); return assertResolved(actual, 2); }); @@ -1283,6 +1277,7 @@ describe('Fantasy-Land Compliance', function(){ const of = Future[FL.of]; const I = x => x; + const T = x => f => f(x); const B = f => g => x => f(g(x)); const sub3 = x => x - 3; @@ -1312,23 +1307,23 @@ describe('Fantasy-Land Compliance', function(){ describe('Apply', () => { test('composition', x => eq( - of(sub3)[FL.map](B)[FL.ap](of(mul3))[FL.ap](of(x)), - of(sub3)[FL.ap](of(mul3)[FL.ap](of(x))) + of(x)[FL.ap](of(sub3)[FL.ap](of(mul3)[FL.map](B))), + of(x)[FL.ap](of(sub3))[FL.ap](of(mul3)) )); }); describe('Applicative', () => { test('identity', x => eq( - of(x), - of(I)[FL.ap](of(x)) + of(x)[FL.ap](of(I)), + of(x) )); test('homomorphism', x => eq( - of(sub3(x)), - of(sub3)[FL.ap](of(x)) + of(x)[FL.ap](of(sub3)), + of(sub3(x)) )); test('interchange', x => eq( - of(sub3)[FL.ap](of(x)), - of(f => f(x))[FL.ap](of(sub3)) + of(x)[FL.ap](of(sub3)), + of(sub3)[FL.ap](of(T(x))) )); });