Skip to content

Commit

Permalink
Switch the entire library to a functional-first approach
Browse files Browse the repository at this point in the history
  • Loading branch information
Avaq committed Apr 21, 2019
1 parent 5ca8af4 commit 651ae0c
Show file tree
Hide file tree
Showing 165 changed files with 2,452 additions and 4,848 deletions.
48 changes: 7 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ for sponsoring the project.
- [`rejectAfter`: Create a Future that rejects after a timeout](#rejectafter)
- [`do`: Create a "coroutine" using a generator function](#do)
- [`try`: Create a Future using a possibly throwing function](#try)
- [`tryP`: Create a Future using a Promise-returning function](#tryp)
- [`attemptP`: Create a Future using a Promise-returning function](#attemptp)
- [`node`: Create a Future using a Node-style callback](#node)
- [`encase`: Convert a possibly throwing function to a Future function](#encase)
- [`encaseP`: Convert a Promise-returning function to a Future function](#encasep)
Expand All @@ -165,7 +165,7 @@ for sponsoring the project.

<details><summary>Converting between Promises and Futures</summary>

- [`tryP`: Create a Future using a Promise-returning function](#tryp)
- [`attemptP`: Create a Future using a Promise-returning function](#attemptp)
- [`encaseP`: Convert a Promise-returning function to a Future function](#encasep)
- [`promise`: Convert a Future to a Promise](#promise)

Expand Down Expand Up @@ -669,12 +669,12 @@ Future.try(() => data.foo.bar.baz)
//> [TypeError: Cannot read property 'baz' of undefined]
```

#### tryP
#### attemptP

<details><summary><code>tryP :: (() -> Promise e r) -> Future e r</code></summary>
<details><summary><code>attemptP :: (() -> Promise e r) -> Future e r</code></summary>

```hs
tryP :: (() -> Promise e r) -> Future e r
attemptP :: (() -> Promise e r) -> Future e r
```

</details>
Expand All @@ -685,7 +685,7 @@ resolves with its resolution value, or rejects with its rejection reason.
Short for [`Future.encaseP(f, undefined)`](#encasep).

```js
Future.tryP(() => Promise.resolve('Hello'))
Future.attemptP(() => Promise.resolve('Hello'))
.fork(console.error, console.log);
//> "Hello"
```
Expand Down Expand Up @@ -772,7 +772,7 @@ Furthermore; `encaseP2` and `encaseP3` are binary and ternary versions of
var fetchf = Future.encaseP(fetch);

fetchf('https://api.github.com/users/Avaq')
.chain(res => Future.tryP(_ => res.json()))
.chain(res => Future.attemptP(_ => res.json()))
.map(user => user.name)
.fork(console.error, console.log);
//> "Aldwin Vlasblom"
Expand Down Expand Up @@ -1105,16 +1105,6 @@ Future.after(300, null)
//> "hello"
```

With good old `reduce`, we can turn this into an asynchronous `all` function,
where the resulting Future will be the leftmost to reject, or the rightmost to
resolve.

```js
var all = ms => ms.reduce(Future.and, Future.of(0));
all([Future.after(20, 1), Future.of(2)]).value(console.log);
//> 2
```

#### alt

<details><summary><code>alt :: Alt f => f a -> f a -> f a</code></summary>
Expand Down Expand Up @@ -1150,16 +1140,6 @@ Future.rejectAfter(300, new Error('Failed'))
//> "hello"
```

With good old `reduce`, we can turn this into an asynchronous `any` function,
where the resulting Future will be the leftmost to resolve, or the rightmost
to reject.

```js
var any = ms => ms.reduce(Future.alt, Future.reject('empty list'));
any([Future.reject(1), Future.after(20, 2), Future.of(3)]).value(console.log);
//> 2
```

#### finally

<details><summary><code>finally :: Future a c -> Future a b -> Future a b</code></summary>
Expand Down Expand Up @@ -1430,20 +1410,6 @@ Future.after(100, 'hello')
//> "bye"
```

With good old `reduce`, we can turn this into a `first` function, where the
resulting Future will be the first to resolve, or the first to reject.

```js
var first = futures => futures.reduce(Future.race, Future.never);
first([
Future.after(100, 'hello'),
Future.after(50, 'bye'),
Future.rejectAfter(25, 'nope')
])
.fork(console.error, console.log);
//! "nope"
```

#### both

<details><summary><code>both :: Future a b -> Future a c -> Future a (Pair b c)</code></summary>
Expand Down
12 changes: 12 additions & 0 deletions bench/fluture.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ module.exports = require('sanctuary-benchmark')(Old, New, config, {
{}, ({of, map}) => run(map(plus1, of(1)))
],

'run.transform.sync.swap.one': [
{}, ({of, swap}) => run(swap(of(42)))
],

'run.transform.sync.swap.many': [
{}, ({of, swap}) => {
let m = of(1);
for(let i = 0; i < 1000; i++) { m = swap(m); }
run(m);
}
],

'run.transform.sync.chain.one': [
{}, ({of, chain}) => run(chain(compose(of, plus1), of(1)))
],
Expand Down
12 changes: 6 additions & 6 deletions bench/folktale.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ const noop = () => {};
const plus1 = x => x + 1;
const repeat = (n, f) => x => Array.from({length: n}).reduce(f, x);

const map1000 = repeat(1000, m => m.map(plus1));
const chain1000 = repeat(1000, m => m.chain(plus1));
const map1000 = repeat(1000, Future.map(plus1));
const chain1000 = repeat(1000, Future.chain(plus1));

const createTask = x => task(resolver => resolver.resolve(x));
const createFuture = x => Future((rej, res) => res(x));
const consumeTask = m => m.run().listen({onCancelled: noop, onRejected: noop, onResolved: noop});
const consumeFuture = m => m.fork(noop, noop);
const consumeFuture = Future.fork(noop)(noop);

const config = {leftHeader: 'Folktale', rightHeader: 'Fluture'};

Expand All @@ -34,7 +34,7 @@ const right = {
module.exports = require('sanctuary-benchmark')(left, right, config, {

'create.construct': [
{}, ({create}) => repeat(1000, x => create(x))(1)
{}, ({create}) => repeat(1000, create)(1)
],

'create.map': [
Expand All @@ -50,15 +50,15 @@ module.exports = require('sanctuary-benchmark')(left, right, config, {
],

'consume.map.1': [
{}, ({one, consume}) => consume(one.map(plus1))
{}, ({one, consume}) => consume(Future.map(plus1)(one))
],

'consume.map.1000': [
{}, ({mapped, consume}) => consume(mapped)
],

'consume.chain': [
{}, ({create, consume, one}) => consume(one.chain(x => create(x + 1)))
{}, ({create, consume, one}) => consume(Future.chain(x => create(x + 1))(one))
],

});
32 changes: 16 additions & 16 deletions bench/promise.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,50 +12,50 @@ const config = {leftHeader: 'Promise', rightHeader: 'Fluture'};
const def = f => [
{defer: true},
(x, [d]) => f(x).then(() => d.resolve()),
(x, [d]) => f(x).value(() => d.resolve())
(x, [d]) => Future.value(() => d.resolve())(f(x))
];

const PromiseInterop = {
of: x => Promise.resolve(x),
map: (f, p) => p.then(f),
chain: (f, p) => p.then(f),
fold: (f, g, p) => p.then(g, f),
race: (a, b) => Promise.race([a, b]),
after: (n, x) => new Promise(res => setTimeout(res, n, x))
resolve: x => Promise.resolve(x),
map: f => p => p.then(f),
chain: f => p => p.then(f),
fold: f => g => p => p.then(g, f),
race: a => b => Promise.race([a, b]),
after: n => x => new Promise(res => setTimeout(res, n, x))
};

module.exports = require('sanctuary-benchmark')(PromiseInterop, Future, config, {

'of': def(
({of}) => repeat(1000, of, 1)
'resolve': def(
({resolve}) => repeat(1000, resolve, 1)
),

'after': def(
({after}) => after(10, 1),
({after}) => after(10)(1),
),

'map': def(
({map, of}) => repeat(1000, m => map(plus1, m), of(1))
({map, resolve}) => repeat(1000, map(plus1), resolve(1))
),

'fold': def(
({fold, of}) => repeat(1000, m => fold(Left, Right, m), of(1))
({fold, resolve}) => repeat(1000, fold(Left)(Right), resolve(1))
),

'chain.sync': def(
({chain, of}) => repeat(1000, m => chain(x => of(plus1(x)), m), of(1))
({chain, resolve}) => repeat(1000, chain(x => resolve(plus1(x))), resolve(1))
),

'chain.async': def(
({chain, after}) => repeat(5, m => chain(x => after(1, plus1(x)), m), after(1, 1))
({chain, after}) => repeat(5, chain(x => after(1)(plus1(x))), after(1)(1))
),

'race.sync': def(
({race, of}) => repeat(1000, m => race(m, of(2)), of(1))
({race, resolve}) => repeat(1000, race(resolve(2)), resolve(1))
),

'race.async': def(
({race, after}) => repeat(5, m => race(m, after(1, 2)), after(1, 1))
({race, after}) => repeat(5, race(after(1)(2)), after(1)(1))
),

});
Loading

0 comments on commit 651ae0c

Please sign in to comment.