Skip to content

Commit

Permalink
Merge pull request #26 from Avaq/av-fantasy-land-1
Browse files Browse the repository at this point in the history
Support FantasyLand 1.x
  • Loading branch information
Avaq authored Sep 23, 2016
2 parents 38fbf44 + 8c7ab4f commit 8788db8
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 59 deletions.
25 changes: 15 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Fluture

[<img src="https://raw.github.com/fantasyland/fantasy-land/master/logo.png" align="right" width="196" height="196" alt="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)
Expand Down Expand Up @@ -52,6 +50,7 @@ getPackageName('package.json')
## Table of contents

- [Usage](#usage)
- [Interoperability](#interoperability)
- [Documentation](#documentation)
1. [Type signatures](#type-signatures)
1. [Creating Futures](#creating-futures)
Expand Down Expand Up @@ -94,6 +93,13 @@ getPackageName('package.json')
- [Benchmarks](#benchmarks)
- [The name](#the-name)

## Interoperability

[<img src="https://raw.github.com/fantasyland/fantasy-land/master/logo.png" align="right" width="82" height="82" alt="Fantasy Land" />][1]

Fluture implements [FantasyLand 1.x][1] compatible `Functor`, `Bifunctor`,
`Apply`, `Applicative`, `Chain` and `Monad`.

## Documentation

### Type signatures
Expand Down Expand Up @@ -315,18 +321,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
```
Expand Down
46 changes: 19 additions & 27 deletions fluture.js
Original file line number Diff line number Diff line change
Expand Up @@ -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){
Expand Down Expand Up @@ -222,7 +222,8 @@

function check$ap$f(f){
if(!isFunction(f)) throw new TypeError(
`Future#ap can only be used on Future<Function> 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)})`
);
}

Expand Down Expand Up @@ -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)}`
);
}

Expand Down Expand Up @@ -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() };
});
}
Expand Down Expand Up @@ -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){
Expand All @@ -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');
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
39 changes: 17 additions & 22 deletions test/fluture.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -726,14 +720,14 @@ describe('Future', () => {
fs.forEach(f => expect(f).to.throw(TypeError, /Future/));
});

it('throws TypeError when not not called on Future<Function>', () => {
it('throws TypeError when not not called with Future<Function>', () => {
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);
});

Expand All @@ -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);
});

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)))
));
});

Expand Down

0 comments on commit 8788db8

Please sign in to comment.