diff --git a/.eslintrc.json b/.eslintrc.json
index aa026f5c..ed0d07c6 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,7 +1,6 @@
{
"root": true,
"extends": ["warp"],
- "plugins": ["markdown"],
"parserOptions": {
"ecmaVersion": 5,
"sourceType": "module"
@@ -15,21 +14,21 @@
"space-before-blocks": [2, "never"],
"consistent-this": [2, "_this"],
"semi": [2, "always", {"omitLastInOneLineBlock": true}],
- "curly": 0,
- "func-style": 0,
- "prefer-template": 0,
- "prefer-arrow-callback": 0,
- "no-use-before-define": 0,
- "max-statements-per-line": 0,
- "prefer-rest-params": 0,
- "class-methods-use-this": 0,
- "camelcase": 0,
- "indent": 0,
- "func-name-matching": 0,
+ "curly": ["off"],
+ "func-style": ["off"],
+ "prefer-template": ["off"],
+ "prefer-arrow-callback": ["off"],
+ "no-use-before-define": ["off"],
+ "max-statements-per-line": ["off"],
+ "prefer-rest-params": ["off"],
+ "class-methods-use-this": ["off"],
+ "camelcase": ["off"],
+ "indent": ["off"],
+ "func-name-matching": ["off"],
"no-console": 2,
"eqeqeq": [2, "smart"],
- "no-eq-null": 0,
- "no-param-reassign": 0
+ "no-eq-null": ["off"],
+ "no-param-reassign": ["off"]
},
"overrides": [
{
@@ -37,26 +36,11 @@
"env": {"node": true, "mocha": true},
"globals": {"Promise": false},
"rules": {
- "arrow-body-style": 0,
- "no-sequences": 0,
- "max-len": 0,
+ "arrow-body-style": ["off"],
+ "no-sequences": ["off"],
+ "max-len": ["off"],
"space-before-function-paren": ["error", "always"]
}
- },
- {
- "files": ["README.md"],
- "env": {
- "browser": true,
- "node": true
- },
- "globals": {
- "Future": false,
- "S": false,
- "Promise": false
- },
- "rules": {
- "no-console": 0
- }
}
]
}
diff --git a/README.md b/README.md
index f9e59721..95d0e5f1 100644
--- a/README.md
+++ b/README.md
@@ -55,17 +55,17 @@ Besides the module system, no other ES5+ features are used in Fluture's source,
which means that no transpilation is needed after concatenation.
```js
-import {readFile} from 'fs';
-import {node, encase} from 'fluture';
+import {readFile} from 'fs'
+import {node, encase, chain, map, fork} from 'fluture'
-var getPackageName = file =>
- node(done => { readFile(file, 'utf8', done) })
- .chain(encase(JSON.parse))
- .map(x => x.name);
+const getPackageName = file => (
+ node (done => { readFile (file, 'utf8', done) })
+ .pipe (chain (encase (JSON.parse)))
+ .pipe (map (x => x.name))
+)
-getPackageName('package.json')
-.fork(console.error, console.log);
-//> "fluture"
+getPackageName ('package.json')
+.pipe (fork (console.error) (console.log))
```
### CommonJS Module
@@ -74,21 +74,18 @@ Although the Fluture source uses the EcmaScript module system, versions
downloaded from the npm registry include a CommonJS build, which will
automatically be used when loading Fluture with `require`.
-
-
```js
-var fs = require('fs');
-var Future = require('fluture');
+const fs = require ('fs')
+const Future = require ('fluture')
-var getPackageName = function(file){
- return Future.node(function(done){ fs.readFile(file, 'utf8', done) })
- .chain(Future.encase(JSON.parse))
- .map(function(x){ return x.name });
-};
+const getPackageName = function (file) {
+ return Future.node (function (done) { fs.readFile (file, 'utf8', done) })
+ .pipe (Future.chain (Future.encase (JSON.parse)))
+ .pipe (Future.map (function (x) { return x.name }))
+}
-getPackageName('package.json')
-.fork(console.error, console.log);
-//> "fluture"
+getPackageName ('package.json')
+.pipe (Future.fork (console.error) (console.log))
```
### Global Bundle (CDN)
@@ -103,10 +100,9 @@ This script will add `Fluture` to the global scope.
[][FL]
[][6]
-* `Future` implements [Fantasy Land][FL] and [Static Land][6] -compatible
+* `Future` implements [Fantasy Land][FL] 1.0+ -compatible
`Alt`, `Bifunctor`, `Monad`, and `ChainRec`
(`of`, `ap`, `alt`, `map`, `bimap`, `chain`, `chainRec`).
- All versions of Fantasy Land are supported.
* `Future.Par` implements [Fantasy Land 3][FL] and [Static Land][6] -compatible
`Alternative` (`of`, `zero`, `map`, `ap`, `alt`).
* The Future and ConcurrentFuture representatives contain `@@type` properties
@@ -141,24 +137,22 @@ for sponsoring the project.
Creating new Futures
- [`Future`: Create a possibly cancellable Future](#future)
-- [`of`: Create a resolved Future](#of)
+- [`resolve`: Create a resolved Future](#resolve)
- [`reject`: Create a rejected Future](#reject)
- [`after`: Create a Future that resolves after a timeout](#after)
- [`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)
+- [`go`: Create a "coroutine" using a generator function](#go)
+- [`attempt`: Create a Future using a possibly throwing function](#attempt)
- [`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)
-- [`encaseN`: Convert a Nodeback function to a Future function](#encasen)
Converting between Nodeback APIs and Futures
- [`node`: Create a Future using a Node-style callback](#node)
-- [`encaseN`: Convert a Nodeback function to a Future function](#encasen)
- [`done`: Consume a Future by providing a Nodeback](#done)
@@ -184,7 +178,7 @@ for sponsoring the project.
- [`ap`: Combine the success values of multiple Futures using a function](#ap)
- [`and`: Logical *and* for Futures](#and)
- [`alt`: Logical *or* for Futures](#alt)
-- [`finally`: Run a Future after the previous settles](#finally)
+- [`lastly`: Run a Future after the previous settles](#lastly)
- [`race`: Race two Futures against each other](#race)
- [`both`: Await both success values from two Futures](#both)
- [`parallel`: Await all success values from many Futures](#parallel)
@@ -214,7 +208,7 @@ for sponsoring the project.
Resource management
- [`hook`: Safely create and dispose resources](#hook)
-- [`finally`: Run a Future after the previous settles](#finally)
+- [`lastly`: Run a Future after the previous settles](#lastly)
@@ -233,51 +227,7 @@ for sponsoring the project.
The various function signatures are provided in a small language referred to as
Hindley-Milner notation. Read about [Hindley-Milner in JavaScript][Guide:HM]
-here. On top of the basic Hindley-Milner notation, we use a few additions to
-describe the JavaScript-specific stuff, like [methods](#squiggly-arrows)
-or functions that take [multiple arguments at once](#brackets).
-
-#### Squiggly Arrows
-
-In order to document *methods*, we use the squiggly arrow (`~>`). This
-separates the implicit `this` argument from the other, explicit, arguments.
-For example, the following line signifies a method, as indicated by the
-squiggly arrow:
-
-```hs
-Future.prototype.map :: Future a b ~> (b -> c) -> Future a c
-```
-
-For comparison, the following example shows a regular function:
-
-```hs
-map :: (b -> c) -> Future a b -> Future a c
-```
-
-#### Brackets
-
-Most functions exposed by Fluture are curried. This is reflected in their type
-signatures by using an arrow at each step where partial application is
-possible. For example, the following line signifies a *curried* function,
-because it has an arrow after each function argument:
-
-```hs
-add :: Number -> Number -> Number
-```
-
-We could have chosen to write the above line with "groups of one argument", but
-we usually leave the grouping brackets out for brevity:
-
-```hs
-add :: (Number) -> (Number) -> Number
-```
-
-In order to document functions and methods that are *not* curried, we use
-grouping to show which arguments have to be provided at the same time:
-
-```hs
-add :: (Number, Number) -> Number
-```
+here.
#### Types
@@ -328,28 +278,28 @@ handler by returning it from the computation. Let's see what this looks like:
```js
// We use the Future constructor to create a Future instance.
-var eventualAnswer = Future(function computeTheAnswer(rej, res){
+const eventualAnswer = Future (function computeTheAnswer (rej, res) {
// We give the computer time to think about the answer, which is 42.
- var timeoutId = setTimeout(res, 60000, 42);
+ const timeoutId = setTimeout (res, 60000, 42)
// Here is how we handle cancellation. This signal is received when nobody
// is interested in the answer any more.
- return function onCancel(){
+ return function onCancel () {
// Clearing the timeout releases the resources we were holding.
- clearTimeout(timeoutId);
- };
+ clearTimeout (timeoutId)
+ }
-});
+})
// Now, let's fork our computation and wait for an answer. Forking gives us
// the unsubscribe function.
-var unsubscribe = eventualAnswer.fork(console.error, console.log);
+const unsubscribe = fork (log ('rejection')) (log ('resolution')) (eventualAnswer)
// After some time passes, we might not care about the answer any more.
// Calling unsubscribe will send a cancellation signal back to the source,
// and trigger the onCancel function.
-unsubscribe();
+unsubscribe ()
```
Many natural sources in Fluture have cancellation handlers of their own.
@@ -369,25 +319,26 @@ This means that none of the following operations result in a
`RangeError: Maximum call stack size exceeded`:
```js
-var add1 = x => x + 1;
-var m = Future.of(1);
+> const add1 = x => x + 1
-for(var i = 0; i < 100000; i++){
- m = m.map(add1);
-}
+> const m = resolve (1)
-m.fork(console.error, console.log);
-//> 100001
+> for (const i = 0; i < 100000; i++) {
+. m = map (add1) (m)
+. }
+
+> fork (log ('rejection')) (log ('resolution')) (m)
+[resolution]: 100001
```
```js
-var m = (function recur(x){
- var mx = Future.of(x + 1);
- return x < 100000 ? mx.chain(recur) : mx;
-}(1));
+> const m = (function recur (x) {
+. const mx = resolve (x + 1)
+. return x < 100000 ? chain (recur) (mx) : mx
+. }(1))
-m.fork(console.error, console.log);
-//> 100001
+> fork (log ('rejection')) (log ('resolution')) (m)
+[resolution]: 100001
```
To learn more about memory and stack usage under different types of recursion,
@@ -437,11 +388,13 @@ When using this module with [Sanctuary Def][$] (and [Sanctuary][S] by
extension) one might run into the following issue:
```js
-var S = require('sanctuary');
-var 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.
+> import S from 'sanctuary'
+
+> import {resolve} from 'fluture'
+
+> S.I (resolve (1))
+! TypeError: Since there is no type of which all the above values are members,
+. the type-variable constraint has been violated.
```
This happens because Sanctuary Def needs to know about the types created by
@@ -451,14 +404,18 @@ To let Sanctuary know about these types, we can obtain the type definitions
from [`fluture-sanctuary-types`][FST] and pass them to [`S.create`][S:create]:
```js
-var {create, env} = require('sanctuary');
-var {env: flutureEnv} = require('fluture-sanctuary-types');
-var Future = require('fluture');
+> import sanctuary from 'sanctuary'
+
+> import {env as flutureEnv} from 'fluture-sanctuary-types'
-var S = create({checkTypes: true, env: env.concat(flutureEnv)});
+> import {resolve} from 'fluture'
-S.I(Future.of(1));
-//> Future.of(1)
+> const S = sanctuary.create ({checkTypes: true, env: env.concat (flutureEnv)})
+
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (S.I (resolve (42)))
+[resolution]: 42
```
### Casting Futures
@@ -470,28 +427,26 @@ When [`isFuture`](#isfuture) returns `false`, a conversion is necessary.
Usually the most concise way of doing this is as follows:
```js
-var NoFuture = require('incompatible-future');
-var incompatible = NoFuture.of('Hello');
+> const NoFuture = require ('incompatible-future')
+
+> const incompatible = NoFuture.of ('Hello')
-//Cast the incompatible Future to our version of Future:
-var compatible = Future(incompatible.fork.bind(incompatible));
+> const compatible = Future (incompatible.fork.bind (incompatible))
-compatible.both(Future.of('world')).value(console.log);
-//> ["Hello", "world"]
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (both (compatible) (resolve ('world')))
+[resolution]: ["Hello", "world"]
```
### Creating Futures
#### Future
-Future :: ((a -> Undefined, b -> Undefined) -> Cancel) -> Future a b
-
```hs
Future :: ((a -> Undefined, b -> Undefined) -> Cancel) -> Future a b
```
-
-
Creates a Future with the given computation. A computation is a function which
takes two callbacks. Both are continuations for the computation. The first is
`reject`, commonly abbreviated to `rej`; The second is `resolve`, or `res`.
@@ -502,418 +457,294 @@ Additionally, the computation may return a nullary function containing
cancellation logic. See [Cancellation](#cancellation).
```js
-Future(function computation(reject, resolve){
- setTimeout(resolve, 3000, 'world');
-});
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (Future (function computation (reject, resolve) {
+. setTimeout (resolve, 20, 42)
+. }))
+[resolution]: 42
```
-#### of
-
-of :: b -> Future a b
+#### resolve
```hs
-of :: b -> Future a b
-resolve :: b -> Future a b
-Future.of :: b -> Future a b
-Future.resolve :: b -> Future a b
+resolve :: b -> Future a b
```
-
-
Creates a Future which immediately resolves with the given value.
-This function has an alias `resolve`.
-
```js
-var eventualThing = Future.of('world');
-eventualThing.fork(
- console.error,
- thing => console.log(`Hello ${thing}!`)
-);
-//> "Hello world!"
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (resolve (42))
+[answer]: 42
```
#### reject
-reject :: a -> Future a b
-
```hs
-reject :: a -> Future a b
-Future.reject :: a -> Future a b
+reject :: a -> Future a b
```
-
-
Creates a Future which immediately rejects with the given value.
```js
-var eventualFailure = Future.reject('I got so far!');
-eventualFailure.fork(
- e => console.error('I tried so hard!', e),
- console.log
-);
-//! "I tried so hard! I got so far!"
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (reject ('It broke!'))
+[rejection]: "It broke!"
```
#### after
-after :: Number -> b -> Future a b
-
```hs
after :: Number -> b -> Future a b
```
-
-
-Creates a Future which resolves with the given value after the given number of
-milliseconds.
+Creates a Future which resolves with the given value after
+the given number of milliseconds.
```js
-var eventualThing = Future.after(500, 'world');
-eventualThing.fork(console.error, thing => console.log(`Hello ${thing}!`));
-//> "Hello world!"
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (after (20) (42))
+[resolution]: 42
```
#### rejectAfter
-rejectAfter :: Number -> a -> Future a b
-
```hs
rejectAfter :: Number -> a -> Future a b
```
-
-
Creates a Future which rejects with the given reason after the given number of
milliseconds.
```js
-var eventualError = Future.rejectAfter(500, new Error('Kaputt!'));
-eventualError.fork(err => console.log('Oh no - ' + err.message), console.log);
-//! Oh no - Kaputt!
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (rejectAfter (20) ('It broke!'))
+[rejection]: "It broke!"
```
-#### do
-
-do :: (() -> Iterator) -> Future a b
+#### go
```hs
-do :: (() -> Iterator) -> Future a b
go :: (() -> Iterator) -> Future a b
```
-
-
A way to do `async`/`await` with Futures, similar to Promise Coroutines or
Haskell Do-notation.
Takes a function which returns an [Iterator](#types), commonly a
generator-function, and chains every produced Future over the previous.
-This function has an alias `go`, for environments where `do` is reserved.
-
```js
-var eventualMessage = Future.do(function*(){
- var thing = yield Future.after(300, 'world');
- var message = yield Future.after(300, 'Hello ' + thing);
- return message + '!';
-});
-
-eventualMessage.fork(console.error, console.log);
-//After 600ms:
-//> "Hello world!"
+> fork (log ('rejection')) (log ('resolution')) (go (function*() {
+. const thing = yield after (20) ('world')
+. const message = yield after (20) ('Hello ' + thing)
+. return message + '!'
+. }))
+[resolution]: "Hello world!"
```
-To handle errors inside a `do` procedure, we need to [`fold`](#fold) the error
-into our control domain, I recommend folding into an [`Either`][S:Either]:
+A rejected Future short-circuits the whole coroutine.
```js
-var attempt = Future.fold(S.Left, S.Right);
-var ajaxGet = url => Future.reject('Failed to load ' + url);
+> fork (log ('rejection')) (log ('resolution')) (go (function*() {
+. const thing = yield reject ('It broke!')
+. const message = yield after (20) ('Hello ' + thing)
+. return message + '!'
+. }))
+[rejection]: "It broke!"
+```
-var eventualMessage = Future.do(function*(){
- var e = yield attempt(ajaxGet('/message'));
- return S.either(
- e => `Oh no! ${e}`,
- x => `Yippee! ${x}`,
- e
- );
-});
+To handle rejections *inside* the coroutine, we need to [`fold`](#fold) the
+error into our control domain.
-eventualMessage.fork(console.error, console.log);
-//> "Oh no! Failed to load /message"
-```
+I recommend folding into an [`Either`][S:Either].
-#### try
+```js
+> const control = fold (S.Left) (S.Right)
+
+> fork (log ('rejection')) (log ('resolution')) (go (function*() {
+. const thing = yield control (reject ('It broke!'))
+. return S.either (x => `Oh no! ${x}`)
+. (x => `Yippee! ${x}`)
+. (thing)
+. }))
+[resolution]: "Oh no! It broke!"
+```
-try :: Catchable e (() -> r) -> Future e r
+#### attempt
```hs
-try :: Catchable e (() -> r) -> Future e r
attempt :: Catchable e (() -> r) -> Future e r
```
-
-
Creates a Future which resolves with the result of calling the given function,
or rejects with the error thrown by the given function.
-Short for [`Future.encase(f, undefined)`](#encase).
-
-This function has an alias `attempt`, for environments where `try` is reserved.
+Short for [`encase (f) (undefined)`](#encase).
```js
-var data = {foo: 'bar'};
-Future.try(() => data.foo.bar.baz)
-.fork(console.error, console.log);
-//> [TypeError: Cannot read property 'baz' of undefined]
+> const data = {foo: 'bar'}
+
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (attempt (() => data.foo.bar.baz))
+[rejection]: new TypeError ("Cannot read property 'baz' of undefined")
```
#### attemptP
-attemptP :: (() -> Promise e r) -> Future e r
-
```hs
attemptP :: (() -> Promise e r) -> Future e r
```
-
-
Create a Future which when forked spawns a Promise using the given function and
resolves with its resolution value, or rejects with its rejection reason.
-Short for [`Future.encaseP(f, undefined)`](#encasep).
+Short for [`encaseP (f) (undefined)`](#encasep).
```js
-Future.attemptP(() => Promise.resolve('Hello'))
-.fork(console.error, console.log);
-//> "Hello"
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (attemptP (() => Promise.resolve (42)))
+[resolution]: 42
```
#### node
-node :: (Nodeback e r -> x) -> Future e r
-
```hs
node :: (Nodeback e r -> x) -> Future e r
```
-
-
Creates a Future which rejects with the first argument given to the function,
or resolves with the second if the first is not present.
Note that this function **does not support cancellation**.
-Short for [`Future.encaseN(f, undefined)`](#encasen).
-
```js
-Future.node(done => {
- done(null, 'Hello');
-})
-.fork(console.error, console.log);
-//> "Hello"
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (node (done => done (null, 42)))
+[resolution]: 42
```
#### encase
-encase :: (Catchable e (a -> r)) -> a -> Future e r
-
```hs
-encase :: (Catchable e ((a ) -> r)) -> a -> Future e r
-encase2 :: (Catchable e ((a, b ) -> r)) -> a -> b -> Future e r
-encase3 :: (Catchable e ((a, b, c) -> r)) -> a -> b -> c -> Future e r
+encase :: (Catchable e (a -> r)) -> a -> Future e r
```
-
-
Takes a function and a value, and returns a Future which when forked calls the
function with the value and resolves with the result. If the function throws
-an exception, it is caught and the Future will reject with the exception:
+an exception, it is caught and the Future will reject with the exception.
-Partially applying `encase` with a function `f` allows us to create a "safe"
-version of `f`. Instead of throwing exceptions, the encased version always
-returns a Future when given the remaining argument(s):
-
-Furthermore; `encase2` and `encase3` are binary and ternary versions of
-`encase`, applying two or three arguments to the given function respectively.
+Applying `encase` with a function `f` creates a "safe" version of `f`. Instead
+of throwing exceptions, the encased version always returns a Future.
```js
-var data = '{"foo" = "bar"}';
-var safeJsonParse = Future.encase(JSON.parse);
-safeJsonParse(data).fork(console.error, console.log);
-//! [SyntaxError: Unexpected token =]
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (encase (JSON.parse) ('{"foo" = "bar"}'))
+[rejection]: new SyntaxError ('Unexpected token =')
```
#### encaseP
-encaseP :: ((a) -> Promise e r) -> a -> Future e r
-
```hs
-encaseP :: ((a) -> Promise e r) -> a -> Future e r
-encaseP2 :: ((a, b) -> Promise e r) -> a -> b -> Future e r
-encaseP3 :: ((a, b, c) -> Promise e r) -> a -> b -> c -> Future e r
+encaseP :: ((a) -> Promise e r) -> a -> Future e r
```
-
-
-Allows Promise-returning functions to be turned into Future-returning
-functions.
+Turns Promise-returning functions into Future-returning functions.
Takes a function which returns a Promise, and a value, and returns a Future.
When forked, the Future calls the function with the value to produce the
Promise, and resolves with its resolution value, or rejects with its rejection
reason.
-Furthermore; `encaseP2` and `encaseP3` are binary and ternary versions of
-`encaseP`, applying two or three arguments to the given function respectively.
-
```js
-var fetchf = Future.encaseP(fetch);
-
-fetchf('https://api.github.com/users/Avaq')
-.chain(res => Future.attemptP(_ => res.json()))
-.map(user => user.name)
-.fork(console.error, console.log);
-//> "Aldwin Vlasblom"
+> encaseP (fetch) ('https://api.github.com/users/Avaq')
+. .pipe (chain (encaseP (res => res.json ())))
+. .pipe (map (user => user.name))
+. .pipe (fork (log ('rejection')) (log ('resolution')))
+[resolution]: "Aldwin Vlasblom"
```
-#### encaseN
-
-encaseN :: ((a, Nodeback e r) -> x) -> a -> Future e r
-
-```hs
-encaseN :: ((a, Nodeback e r) -> x) -> a -> Future e r
-encaseN2 :: ((a, b, Nodeback e r) -> x) -> a -> b -> Future e r
-encaseN3 :: ((a, b, c, Nodeback e r) -> x) -> a -> b -> c -> Future e r
-```
-
-
-
-Allows [continuation-passing-style][1] functions to be turned into
-Future-returning functions.
-
-Takes a function which accepts as its last parameter a [Nodeback](#types), and
-a value, and returns a Future. When forked, the Future calls the function with
-the value and a Nodeback and resolves the second argument passed to the
-Nodeback, or or rejects with the first argument.
-
-Furthermore; `encaseN2` and `encaseN3` are binary and ternary versions of
-`encaseN`, applying two or three arguments to the given function respectively.
-
-```js
-var fs = require('fs');
-
-var read = Future.encaseN2(fs.readFile);
-
-read('README.md', 'utf8')
-.map(text => text.split('\n'))
-.map(lines => lines[0])
-.fork(console.error, console.log);
-//> "# [![Fluture](logo.png)](#butterfly)"
-```
-
-#### chainRec
-
-Future.chainRec :: ((a -> Next a, b -> Done b, a) -> Future e (Next a | Done b), a) -> Future e b
-
-```hs
-Future.chainRec :: ((a -> Next a, b -> Done b, a) -> Future e (Next a | Done b), a) -> Future e b
-```
-
-
-
-Implementation of [Fantasy Land ChainRec][FL:chainrec]. Since Fluture 6.0
-introduced [stack safety](#stack-safety) there should be no need to use this
-function directly. Instead it's recommended to use [`chain(rec)`](#chain).
-
### Transforming Futures
#### map
-map :: Functor m => (a -> b) -> m a -> m b
-
```hs
-map :: Functor m => (a -> b) -> m a -> m b
-Future.map :: Functor m => (a -> b) -> m a -> m b
-Par.map :: Functor m => (a -> b) -> m a -> m b
-Future.prototype.map :: Future e a ~> (a -> b) -> Future e b
+map :: Functor m => (a -> b) -> m a -> m b
```
-
-
-Transforms the resolution value inside the Future, and returns a Future with
-the new value. The transformation is only applied to the resolution branch: if
-the Future is rejected, the transformation is ignored.
+Transforms the resolution value inside the Future or [Functor][FL:functor],
+and returns a Future or Functor with the new value. The transformation is only
+applied to the resolution branch: if the Future is rejected, the transformation
+is ignored.
See also [`chain`](#chain) and [`mapRej`](#maprej).
```js
-Future.of(1)
-.map(x => x + 1)
-.fork(console.error, console.log);
-//> 2
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (map (x => x + 1) (resolve (41)))
+[resolution]: 42
```
For comparison, the equivalent with Promises is:
```js
-Promise.resolve(1)
-.then(x => x + 1)
-.then(console.log, console.error);
+> Promise.resolve (41)
+. .then (x => x + 1)
+. .then (log ('resolution'), log ('rejection'))
+[resolution]: 42
```
#### bimap
-bimap :: Bifunctor m => (a -> c) -> (b -> d) -> m a b -> m c d
-
```hs
-bimap :: Bifunctor m => (a -> c) -> (b -> d) -> m a b -> m c d
-Future.bimap :: Bifunctor m => (a -> c) -> (b -> d) -> m a b -> m c d
-Future.prototype.bimap :: Future a b ~> (a -> c, b -> d) -> Future c d
+bimap :: Bifunctor m => (a -> c) -> (b -> d) -> m a b -> m c d
```
-
-
-Maps the left function over the rejection value, or the right function over the
-resolution value, depending on which is present.
+Maps the left function over the rejection reason, or the right function over
+the resolution value, depending on which is present. Can be used on any
+[Bifunctor][FL:bifunctor].
```js
-Future.of(1)
-.bimap(x => x + '!', x => x + 1)
-.fork(console.error, console.log);
-//> 2
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (bimap (x => x + '!') (x => x + 1) (resolve (41)))
+[resolution]: 42
-Future.reject('error')
-.bimap(x => x + '!', x => x + 1)
-.fork(console.error, console.log);
-//! "error!"
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (bimap (x => x + '!') (x => x + 1) (reject ('It broke!')))
+[rejection]: "It broke!!"
```
For comparison, the equivalent with Promises is:
```js
-Promise.resolve(1)
-.then(x => x + 1, x => x + '!')
-.then(console.log, console.error);
+> Promise.resolve (41)
+. .then (x => x + 1, x => x + '!')
+. .then (log ('resolution'), log ('rejection'))
+[resolution]: 42
```
#### chain
-chain :: Chain m => (a -> m b) -> m a -> m b
-
```hs
-chain :: Chain m => (a -> m b) -> m a -> m b
-Future.chain :: Chain m => (a -> m b) -> m a -> m b
-Future.prototype.chain :: Future e a ~> (a -> Future e b) -> Future e b
+chain :: Chain m => (a -> m b) -> m a -> m b
```
-
-
-Sequence a new Future using the resolution value from another. Similarly to
-[`map`](#map), `chain` expects a function to transform the resolution value of
-a Future. But instead of returning the new *value*, chain expects a Future to
-be returned.
+Sequence a new Future or [Chain][FL:chain] using the resolution value from
+another. Similarly to [`map`](#map), `chain` expects a function. But instead
+of returning the new *value*, chain expects a Future (or instance of the same
+Chain) to be returned.
The transformation is only applied to the resolution branch: if the Future is
rejected, the transformation is ignored.
@@ -921,110 +752,97 @@ rejected, the transformation is ignored.
See also [`chainRej`](#chainrej).
```js
-Future.of(1)
-.chain(x => Future.of(x + 1))
-.fork(console.error, console.log);
-//> 2
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (chain (x => resolve (x + 1)) (resolve (41)))
+[resolution]: 42
```
For comparison, the equivalent with Promises is:
```js
-Promise.resolve(1)
-.then(x => Promise.resolve(x + 1))
-.then(console.log, console.error);
+> Promise.resolve (41)
+. .then (x => Promise.resolve (x + 1))
+. .then (log ('resolution'), log ('rejection'))
+[resolution]: 42
```
#### swap
-swap :: Future a b -> Future b a
-
```hs
-swap :: Future a b -> Future b a
-Future.prototype.swap :: Future a b ~> Future b a
+swap :: Future a b -> Future b a
```
-
-
-Resolve with the rejection reason, or reject with the resolution value.
+Swap the rejection and resolution branches.
```js
-Future.of(new Error('It broke')).swap().fork(console.error, console.log);
-//! [It broke]
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (swap (resolve (42)))
+[rejection]: 42
-Future.reject('Nothing broke').swap().fork(console.error, console.log);
-//> "Nothing broke"
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (swap (reject (42)))
+[resolution]: 42
```
#### mapRej
-mapRej :: (a -> c) -> Future a b -> Future c b
-
```hs
-mapRej :: (a -> c) -> Future a b -> Future c b
-Future.prototype.mapRej :: Future a b ~> (a -> c) -> Future c b
+mapRej :: (a -> c) -> Future a b -> Future c b
```
-
-
Map over the **rejection** reason of the Future. This is like [`map`](#map),
but for the rejection branch.
```js
-Future.reject(new Error('It broke!'))
-.mapRej(err => new Error('Oh No! ' + err.message))
-.fork(console.error, console.log);
-//! [Oh No! It broke!]
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (mapRej (s => `Oh no! ${s}`) (reject ('It broke!')))
+[rejection]: "Oh no! It broke!"
```
For comparison, the equivalent with Promises is:
```js
-Promise.resolve(1)
-.then(null, err => Promise.reject(new Error('Oh No! ' + err.message)))
-.then(console.log, console.error);
+> Promise.reject ('It broke!')
+. .then (null, s => Promise.reject (`Oh no! ${s}`))
+. .then (log ('resolution'), log ('rejection'))
+[rejection]: "Oh no! It broke!"
```
#### chainRej
-chainRej :: (a -> Future c b) -> Future a b -> Future c b
-
```hs
-chainRej :: (a -> Future c b) -> Future a b -> Future c b
-Future.prototype.chainRej :: Future a b ~> (a -> Future c b) -> Future c b
+chainRej :: (a -> Future c b) -> Future a b -> Future c b
```
-
-
Chain over the **rejection** reason of the Future. This is like
[`chain`](#chain), but for the rejection branch.
```js
-Future.reject(new Error('It broke!'))
-.chainRej(err => Future.of(err.message + ' But it\'s all good.'))
-.fork(console.error, console.log);
-//> "It broke! But it's all good."
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (chainRej (s => resolve (`${s} But it's all good.`)))
+[resolution]: "It broke! But it's all good."
```
For comparison, the equivalent with Promises is:
```js
-Promise.reject(new Error('It broke!'))
-.then(null, err => err.message + ' But it\'s all good.')
-.then(console.log, console.error);
+> Promise.reject ('It broke!')
+. .then (null, s => `${s} But it's all good.`)
+. .then (log ('resolution'), log ('rejection'))
+[resolution]: "It broke! But it's all good."
```
#### fold
-fold :: (a -> c) -> (b -> c) -> Future a b -> Future d c
-
```hs
-fold :: (a -> c) -> (b -> c) -> Future a b -> Future d c
-Future.prototype.fold :: Future a b ~> (a -> c, b -> c) -> Future d c
+fold :: (a -> c) -> (b -> c) -> Future a b -> Future d c
```
-
-
Applies the left function to the rejection value, or the right function to the
resolution value, depending on which is present, and resolves with the result.
@@ -1033,175 +851,151 @@ be used with other type constructors, like [`S.Either`][S:Either], to maintain
a representation of failure.
```js
-Future.of('hello')
-.fold(S.Left, S.Right)
-.value(console.log);
-//> Right('hello')
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (fold (S.Left) (S.Right) (resolve ('hello'))
+[resolution]: Right ("hello")
-Future.reject('it broke')
-.fold(S.Left, S.Right)
-.value(console.log);
-//> Left('it broke')
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (fold (S.Left) (S.Right) (reject ('It broke!'))
+[resolution]: Left ("It broke!")
```
For comparison, the equivalent with Promises is:
```js
-Promise.resolve('hello')
-.then(S.Right, S.Left)
-.then(console.log);
+> Promise.resolve ('hello')
+. .then (S.Right, S.Left)
+. .then (log ('resolution'), log ('rejection'))
+[resolution]: Right ("hello")
```
### Combining Futures
#### ap
-ap :: Apply m => m (a -> b) -> m a -> m b
-
```hs
-ap :: Apply m => m (a -> b) -> m a -> m b
-Future.ap :: Apply m => m (a -> b) -> m a -> m b
-Par.ap :: Apply m => m (a -> b) -> m a -> m b
-Future.prototype.ap :: Future e (a -> b) ~> Future e a -> Future e b
+ap :: Apply m => m a -> m (a -> b) -> m b
```
-
+Applies the function contained in the right-hand Future or [Apply][FL:apply]
+to the value contained in the left-hand Future or Apply. This process can be
+repeated to gradually fill out multiple function arguments of a curried
+function, as shown below.
+
+Note that the Futures will be executed in sequence - not in parallel\* -
+because of the Monadic nature of Futures. The execution order is, as
+specified by Fantasy Land, `m (a -> b)` first followed by `m a`.
+So that's *right before left*.
-Applies the function contained in the left-hand Future to the value
-contained in the right-hand Future. If one of the Futures rejects the
-resulting Future will also be rejected.
+\* If you'd like to use a parallel implementation of `ap`, you need to use a
+ non-monadic type. Fluture provides this through [`Par`](#concurrentfuture).
+ Wrapping your Future instances with `Par` before passing them to `ap` will
+ cause them to run in parallel!
```js
-Future.of(x => y => x + y)
-.ap(Future.of(1))
-.ap(Future.of(2))
-.fork(console.error, console.log);
-//> 3
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (ap (resolve (7)) (ap (resolve (49)) (resolve (x => y => x - y))))
+[resolution]: 42
```
-#### and
-
-and :: Future a b -> Future a c -> Future a c
+#### alt
```hs
-and :: Future a b -> Future a c -> Future a c
-Future.prototype.and :: Future a b ~> Future a c -> Future a c
+alt :: Alt f => f a -> f a -> f a
```
-
+Select one of two [Alts](#types).
-Logical *and* for Futures.
+Behaves like logical *or* on [`Future`](#future) instances, returning a new
+Future which either resolves with the first resolution value, or rejects with
+the last rejection reason. We can use it if we want a computation to run only
+if another has failed.
-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. We
-can use it if we want a computation to run only after another has succeeded.
+Note that the Futures will be executed in sequence - not in parallel\* -
+because of the Monadic nature of Futures. The *right* Future is evaluated
+before the *left* Future.
+
+See also [`and`](#and) and [`lastly`](#lastly).
-See also [`alt`](#alt) and [`finally`](#finally).
+\* If you'd like to use a parallel implementation of `alt`, you could simply
+ use [`race`](#race). Alternatively you could wrap your Future instances
+ with [`Par`](#concurrentfuture) before passing them to `alt`.
```js
-Future.after(300, null)
-.and(Future.of('hello'))
-.fork(console.error, console.log);
-//> "hello"
-```
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (alt (resolve ('left')) (resolve ('right')))
+[resolution]: "right"
-#### alt
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (alt (resolve ('left')) (reject ('It broke!')))
+[resolution]: "left"
+```
-alt :: Alt f => f a -> f a -> f a
+#### and
```hs
-alt :: Alt f => f a -> f a -> f a
-or :: Alt f => f a -> f a -> f a
-Future.alt :: Alt f => f a -> f a -> f a
-Par.alt :: Alt f => f a -> f a -> f a
-Future.prototype.alt :: Future a b ~> Future a b -> Future a b
-Future.prototype.or :: Future a b ~> Future a b -> Future a b
+and :: Future a c -> Future a b -> Future a c
```
-
-
-Select one of two [Alts](#types).
-
-Behaves like logical *or* on [`Future`](#future) instances, returning a new
-Future which either resolves with the first resolution value, or rejects with
-the last rejection reason. We can use it if we want a computation to run only
-if another has failed.
-
-Behaves like [`race`](#race) on [`ConcurrentFuture`](#concurrentfuture) instances.
+Logical *and* for Futures.
-This function has an alias `or` for legacy reasons.
+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. We
+can use it if we want a computation to run only after another has succeeded.
+The *right* Future is evaluated before the *left* Future.
-See also [`and`](#and) and [`finally`](#finally).
+See also [`alt`](#alt) and [`lastly`](#lastly).
```js
-Future.rejectAfter(300, new Error('Failed'))
-.alt(Future.of('hello'))
-.fork(console.error, console.log);
-//> "hello"
-```
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (and (resolve ('left')) (resolve ('right')))
+[resolution]: "left"
-#### finally
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (and (resolve ('left')) (reject ('It broke!')))
+[rejection]: "It broke!"
+```
-finally :: Future a c -> Future a b -> Future a b
+#### lastly
```hs
-finally :: Future a c -> Future a b -> Future a b
-lastly :: Future a c -> Future a b -> Future a b
-Future.prototype.finally :: Future a b ~> Future a c -> Future a b
-Future.prototype.lastly :: Future a b ~> Future a c -> Future a b
+lastly :: Future a c -> Future a b -> Future a b
```
-
-
Run a second Future after the first settles (successfully or unsuccessfully).
Rejects with the rejection reason from the first or second Future, or resolves
-with the resolution value from the first Future. We can use this when we want
-a computation to run after another settles, successfully or unsuccessfully.
+with the resolution value from the first Future. This can be used to run a
+computation after another settles, successfully or unsuccessfully.
If you're looking to clean up resources after running a computation which
acquires them, you should use [`hook`](#hook), which has many more fail-safes
in place.
-This function has an alias `lastly`, for environments where `finally` is
-reserved.
-
See also [`and`](#and) and [`alt`](#alt).
```js
-Future.of('Hello')
-.finally(Future.of('All done!').map(console.log))
-.fork(console.error, console.log);
-//> "All done!"
-//> "Hello"
-```
-
-Note that the *first* Future is given as the *last* argument to `Future.finally()`:
-
-```js
-var program = S.pipe([
- Future.of,
- Future.finally(Future.of('All done!').map(console.log)),
- Future.fork(console.error, console.log)
-]);
-
-program('Hello');
-//> "All done!"
-//> "Hello"
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (lastly (encase (log ('lastly')) ('All done!')) (resolve (42)))
+[lastly]: "All done!"
+[resolution]: 42
```
### Consuming Futures
#### fork
-fork :: (a -> Any) -> (b -> Any) -> Future a b -> Cancel
-
```hs
-fork :: (a -> Any) -> (b -> Any) -> Future a b -> Cancel
-Future.prototype.fork :: Future a b ~> (a -> Any, b -> Any) -> Cancel
+fork :: (a -> Any) -> (b -> Any) -> Future a b -> Cancel
```
-
-
Execute the computation represented by a Future, passing `reject` and `resolve`
callbacks to continue once there is a result.
@@ -1215,42 +1009,22 @@ program.
After we `fork` a Future, the computation will start running. If the program
decides halfway through that it's no longer interested in the result of the
-computation, it can call the `unsubscribe` function returned by `fork()`. See
+computation, it can call the `unsubscribe` function returned by `fork`. See
[Cancellation](#cancellation).
-Note that if an exception was encountered during the computation, it will be
-thrown and likely not be catchable. If the computation ran in isolation, we may
-want to use [`forkCatch`](#forkcatch) instead to recover from exceptions.
-
-```js
-Future.of('world').fork(
- err => console.log(`Oh no! ${err.message}`),
- thing => console.log(`Hello ${thing}!`)
-);
-//> "Hello world!"
-
-Future.reject(new Error('It broke!')).fork(
- err => console.log(`Oh no! ${err.message}`),
- thing => console.log(`Hello ${thing}!`)
-);
-//! "Oh no! It broke!"
+If an exception was encountered during the computation, it will be re-thrown
+by `fork` and likely not be catchable. You can handle it using
+`process.on('uncaughtException')` in Node, or use [`forkCatch`](#forkcatch).
-var consoleFork = Future.fork(console.error, console.log);
-consoleFork(Future.of('Hello'));
-//> "Hello"
-```
+Almost all code examples in Fluture use `fork` to run the computation. There
+are some variations on `fork` that serve different purposes below.
#### forkCatch
-forkCatch :: (Error -> Any) -> (a -> Any) -> (b -> Any) -> Future a b -> Cancel
-
```hs
-forkCatch :: (Error -> Any) -> (a -> Any) -> (b -> Any) -> Future a b -> Cancel
-Future.prototype.forkCatch :: Future a b ~> (Error -> Any, a -> Any, b -> Any) -> Cancel
+forkCatch :: (Error -> Any) -> (a -> Any) -> (b -> Any) -> Future a b -> Cancel
```
-
-
An advanced version of [fork](#fork) that allows us to react to a fatal error
in a custom way. Fatal errors occur when unexpected exceptions are thrown, when
the Fluture API is used incorrectly, or when resources couldn't be disposed.
@@ -1274,55 +1048,38 @@ See [Debugging](#debugging) for information about the Error object that is
passed to your exception handler.
```js
-var fut = Future.after(300, null).map(x => x.foo);
-fut.forkCatch(e => {
- console.error('fatal error:', e.stack);
- console.error('caused in: ', e.future.toString());
- process.exit(1);
-}, console.error, console.log);
-//! fatal error: Cannot read property 'foo' of null
-//! at ...
-//! caused in: Future.after(300, null).map(x => x.foo)
+> forkCatch (log ('fatal error'))
+. (log ('rejection'))
+. (log ('resolution'))
+. (map (x => x.foo) (resolve (null)))
+[fatal error]: new Error ("Cannot read property 'foo' of null")
```
#### value
-value :: (b -> x) -> Future a b -> Cancel
-
```hs
-value :: (b -> x) -> Future a b -> Cancel
-Future.prototype.value :: Future a b ~> (b -> x) -> Cancel
+value :: (b -> Any) -> Future a b -> Cancel
```
-
-
-Extracts the value from a resolved Future by forking it. Only use this function
+Like [`fork`](#fork) but for the resolution branch only. Only use this function
if you are sure the Future is going to be resolved, for example; after using
-[`fold`](#fold). If the Future rejects and `value` was used, an uncatchable
-`Error` will be thrown.
-
-```js
-Future.reject(new Error('It broke'))
-.fold(S.Left, S.Right)
-.value(console.log);
-//> Left([Error: It broke])
-```
+[`fold`](#fold). If the Future rejects, `value` will throw an Error.
As with [`fork`](#fork), `value` returns an `unsubscribe` function. See
[Cancellation](#cancellation).
-#### done
+```js
+> value (log ('resolution')) (resolve (42))
+[resolution]: 42
+```
-done :: Nodeback a b -> Future a b -> Cancel
+#### done
```hs
-done :: Nodeback a b -> Future a b -> Cancel
-Future.prototype.done :: Future a b ~> Nodeback a b -> Cancel
+done :: Nodeback a b -> Future a b -> Cancel
```
-
-
-Fork the Future into a [Nodeback](#types).
+Run the Future using a [Nodeback](#types) as the continuation.
This is like [`fork`](#fork), but instead of taking two unary functions, it
takes a single binary function.
@@ -1331,155 +1088,109 @@ As with [`fork`](#fork), `done` returns an `unsubscribe` function. See
[Cancellation](#cancellation).
```js
-Future.of('hello').done((err, val) => console.log(val));
-//> "hello"
+> done ((err, val) => log ('resolution') (val)) (resolve (42))
+[resolution]: 42
```
#### promise
-promise :: Future a b -> Promise b a
-
```hs
-promise :: Future a b -> Promise b a
-Future.prototype.promise :: Future a b ~> Promise b a
+promise :: Future a b -> Promise b a
```
-
+Run the Future and get a Promise to represent its continuation.
-An alternative way to [`fork`](#fork) the Future. Returns a Promise which
-resolves with the resolution value, or rejects with the rejection reason of
-the Future.
+Returns a Promise which resolves with the resolution value, or rejects with
+the rejection reason of the Future.
-Note that if an exception was encountered during the computation, it will be
-thrown and likely not be catchable.
-
-```js
-Future.of('Hello').promise().then(console.log);
-//> "Hello"
-```
+If an exception was encountered during the computation, it will be thrown.
This is a convenience function which provides a "quick and dirty" way to create
a Promise from a Future. You should only use it in scenarios where you're not
interested in [cancellation](#cancellation), nor interested in recovering from
exceptions. For example in a test runner that wants you to give it a Promise.
-In any other scenario, if you *really* want a Promise, you should probably
-make a custom wrapper around [`forkCatch`](#forkcatch) to create your Promise,
-for example:
```js
-const eventualThing = Future.after(300, 'World');
-
-new Promise((res, rej) => {
-
- // We've decided that an exception should go to the rejection branch, and
- // we're wrapping the failure or success values to not lose information.
- const cancel = eventualThing.forkCatch(rej, reason => {
- res({success: false, reason: reason, value: null});
- }, value => {
- res({success: true, reason: null, value: value});
- });
-
- // We're also handling cancellation here.
- process.on('SIGINT', cancel);
-
-});
+> promise (resolve (42)) .then (log ('resolution'))
+[resolution]: 42
```
### Parallelism
#### race
-race :: Future a b -> Future a b -> Future a b
-
```hs
-race :: Future a b -> Future a b -> Future a b
-Future.prototype.race :: Future a b ~> Future a b -> Future a b
+race :: Future a b -> Future a b -> Future a b
```
-
-
Race two Futures against each other. Creates a new Future which resolves or
rejects with the resolution or rejection value of the first Future to settle.
When one Future settles, the other gets cancelled automatically.
```js
-Future.after(100, 'hello')
-.race(Future.after(50, 'bye'))
-.fork(console.error, console.log);
-//> "bye"
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (race (after (15) ('left')) (after (30) ('right')))
+[resolution]: "left"
```
#### both
-both :: Future a b -> Future a c -> Future a (Pair b c)
-
```hs
-both :: Future a b -> Future a c -> Future a (Pair b c)
-Future.prototype.both :: Future a b ~> Future a c -> Future a (Pair b c)
+both :: Future a b -> Future a c -> Future a (Pair b c)
```
-
-
Run two Futures in parallel and get a [`Pair`](#types) of the results. When
either Future rejects, the other Future will be cancelled and the resulting
Future will reject.
```js
-var a = Future.of('a');
-var b = Future.of('b');
-
-Future.both(a, b).fork(console.error, console.log);
-//> ['a', 'b']
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (both (after (15) ('left')) (after (30) ('right')))
+[resolution]: ["left", "right"]
```
#### parallel
-parallel :: PositiveInteger -> Array (Future a b) -> Future a (Array b)
-
```hs
parallel :: PositiveInteger -> Array (Future a b) -> Future a (Array b)
```
-
-
Creates a Future which when forked runs all Futures in the given Array in
parallel, ensuring no more than `limit` Futures are running at once.
-When one Future rejects, all currently running Futures will be cancelled and
-the resulting Future will reject.
-
-```js
-var tenFutures = Array.from(Array(10).keys()).map(Future.after(20));
+In the following example, we're running up to 5 Futures in parallel. Every
+Future takes about 20ms to settle, which means the result should appear after
+about 40ms.
-//Runs all Futures in sequence:
-Future.parallel(1, tenFutures).fork(console.error, console.log);
-//after about 200ms:
-//> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+If we use `1` for the limit, the Futures would run in sequence, causing the
+result to appear only after 200ms.
-//Runs upto five Futures in parallel:
-Future.parallel(5, tenFutures).fork(console.error, console.log);
-//after about 40ms:
-//> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+We can also use `Infinity` as the limit. This would create a function similar
+to `Promise.all`, which always runs all Futures in parallel. This can easily
+cause the computation to consume too many resources, however, so I would
+advise using a number roughly equal to maximum size of Array you think your
+program should handle.
-//Runs all Futures in parallel:
-Future.parallel(Infinity, tenFutures).fork(console.error, console.log);
-//after about 20ms:
-//> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+```js
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (parallel (5) (Array.from (Array (10) .keys ()) .map (after (20))))
+[resolution]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```
-If you want to settle all Futures, even if some may fail, you can use this in
-combination with [fold](#fold):
+When one Future rejects, all currently running Futures will be cancelled and
+the resulting Future will reject. If you want to settle all Futures, even if
+some may fail, you can use `parallel` in combination with [fold](#fold).
```js
-var unstableFutures = Array.from({length: 4}, (_, i) =>
- Future.node(done => done(Math.random() > 0.75 ? 'failed' : null, i))
-);
-
-var stabalizedFutures = unstableFutures.map(Future.fold(S.Left, S.Right));
-
-Future.parallel(Infinity, stabalizedFutures).fork(console.error, console.log);
-//> [ Right(0), Left("failed"), Right(2), Right(3) ]
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (parallel (2) ([resolve (42), reject ('It broke!')]
+. .map (fold (S.Left) (S.Right))))
+[resolution]: [Right (42), Left ("It broke!")]
```
#### ConcurrentFuture
@@ -1496,75 +1207,46 @@ concurrent behaviour respectively. It's a useful type to pass to abstractions
that don't know about Future-specific functions like [`parallel`](#parallel) or
[`race`](#race), but *do* know how to operate on Apply and Alternative.
-```js
-var {of, ap, sequence} = require('sanctuary');
-var {Future, Par, seq} = require('fluture');
+See also [`ap`](#ap) and [`alt`](#alt).
+```js
//Some dummy values
-var x = 1;
-var f = a => a + 1;
+const x = 41;
+const f = a => a + 1;
//The following two are equal ways to construct a ConcurrentFuture
-var parx = of(Par, x);
-var parf = Par(of(Future, f));
+const parx = S.of (Par) (x)
+const parf = Par (S.of (Future) (f))
//We can make use of parallel apply
-seq(ap(parx, parf)).value(console.log);
-//> 2
+value (log ('resolution')) (seq (ap (parx) (parf)))
+[resolution]: 42
-//Or concurrent sequencing
-seq(sequence(Par, [parx, parf])).value(console.log);
-//> [x, f]
+//Concurrent sequencing
+value (log ('resolution')) (seq (S.sequence (Par) ([parx, parx, parx])))
+[resolution]: [41, 41, 41]
+
+//And concurrent alt
+value (log ('resolution')) (alt (after (15) ('left')) (after (30) ('right')))
+[resolution]: "left"
```
##### Par
-Converts a Future to a ConcurrentFuture.
-
-Par :: Future a b -> ConcurrentFuture a b
-
```hs
Par :: Future a b -> ConcurrentFuture a b
```
-
-
-##### Par.of
-
-Constructs a ConcurrentFuture with the given resolution value.
-
-Par.of :: b -> ConcurrentFuture a b
-
-```hs
-Par.of :: b -> ConcurrentFuture a b
-```
-
-
-
-##### Par.zero
-
-Constructs a ConcurrentFuture which will never resolve or reject with anything.
-
-Par.zero :: () -> ConcurrentFuture a a
-
-```hs
-Par.zero :: () -> ConcurrentFuture a a
-```
-
-
+Converts a Future to a ConcurrentFuture.
##### seq
Converts a ConcurrentFuture to a Future.
-seq :: ConcurrentFuture a b -> Future a b
-
```hs
seq :: ConcurrentFuture a b -> Future a b
```
-
-
### Resource management
Functions listed under this category allow for more fine-grained control over
@@ -1572,19 +1254,15 @@ the flow of acquired values.
#### hook
-hook :: Future a b -> (b -> Future c d) -> (b -> Future a e) -> Future a e
-
```hs
hook :: Future a b -> (b -> Future c d) -> (b -> Future a e) -> Future a e
```
-
-
Combines resource acquisition, consumption, and disposal in such a way that you
can be sure that a resource will always be disposed if it was acquired, even if
an exception is thrown during consumption; Sometimes referred to as bracketing.
-The signature is like `hook(acquire, dispose, consume)`, where:
+The signature is like `hook (acquire, dispose, consume)`, where:
- `acquire` is a Future which might create connections, open files, etc.
- `dispose` is a function that takes the result from `acquire` and should be
@@ -1597,15 +1275,18 @@ The signature is like `hook(acquire, dispose, consume)`, where:
Typically, you'd want to partially apply this function with the first two
arguments (acquisition and disposal), as shown in the example.
-
```js
-var withConnection = Future.hook(
- openConnection('localhost'),
- closeConnection
-);
+> import {open, read, close} from 'fs'
-withConnection(conn => query(conn, 'SELECT * FROM users'))
-.fork(console.error, console.log);
+> const withFile = hook (node (done => open ('package.json', 'r', done)))
+. (fd => node (done => close (fd, done)))
+
+> fork (log ('rejection'))
+. (log ('resolution'))
+. (withFile (fd => node (done => (
+. read (fd, Buffer.alloc (1), 0, 1, null, (e, _, x) => done (e, x)))
+. )))
+[resolution]:
```
When a hooked Future is cancelled while acquiring its resource, nothing else
@@ -1620,38 +1301,30 @@ multiple hooks into one.
#### pipe
-pipe :: Future a b ~> (Future a b -> c) -> c
-
```hs
Future.prototype.pipe :: Future a b ~> (Future a b -> c) -> c
```
-
-
A method available on all Futures to allow arbitrary functions over Futures to
be included in a fluent-style method chain.
-This method is particularly useful in combination with functions derived from
-Fantasy Land implementations, for example [`S.join`][S:join]:
+You can think of this as a fallback for the [ESNext pipe operator (`|>`)][2].
```js
-Future.of(42)
-.map(Future.resolve)
-.pipe(S.join)
-.value(console.log);
-//> 42
+> resolve (x => y => x * y)
+. .pipe (ap (after (20) (Math.PI)))
+. .pipe (ap (after (20) (13.37)))
+. .pipe (map (Math.round))
+. .pipe (fork (log ('rejection')) (log ('resolution')))
+[resolution]: 42
```
#### cache
-cache :: Future a b -> Future a b
-
```hs
cache :: Future a b -> Future a b
```
-
-
Returns a Future which caches the resolution value or rejection reason of the
given Future so that whenever it's forked, it can load the value from cache
rather than re-executing the underlying computation.
@@ -1661,94 +1334,84 @@ multiple consumers to subscribe to the same result. The underlying computation
is never [cancelled](#cancellation) unless *all* consumers unsubscribe before
it completes.
-There is a glaring drawback to using `cache`, which is that returned Futures
-are no longer referentially transparent, making reasoning about them more
-difficult and refactoring code that uses them harder.
+**There is a glaring drawback to using `cache`**, which is that returned
+Futures are no longer referentially transparent, making reasoning about them
+more difficult and refactoring code that uses them harder.
```js
-var {readFile} = require('fs');
-var eventualPackage = Future.cache(
- Future.node(done => {
- console.log('Reading some big data');
- readFile('package.json', 'utf8', done);
- })
-);
+> import {readFile} from 'fs'
+
+> const eventualPackageName = (
+. node (done => readFile ('package.json', 'utf8', done))
+. .pipe (chain (encase (JSON.parse)))
+. .pipe (chain (encase (x => x.name)))
+. .pipe (map (data => {
+. log ('debug') ('Read, parsed, and traversed the package data')
+. return data
+. }))
+. )
+
+> fork (log ('rejection')) (log ('resolution')) (eventualPackageName)
+[debug]: "Read, parsed, and traversed the package data"
+[resolution]: "Fluture"
-eventualPackage.fork(console.error, console.log);
-//> "Reading some big data"
-//> "{...}"
+> fork (log ('rejection')) (log ('resolution')) (eventualPackageName)
+[debug]: "Read, parsed, and traversed the package data"
+[resolution]: "Fluture"
-eventualPackage.fork(console.error, console.log);
-//> "{...}"
+> const eventualCachedPackageName = cache (eventualPackageName)
+
+> fork (log ('rejection')) (log ('resolution')) (eventualCachedPackageName)
+[debug]: "Read, parsed, and traversed the package data"
+[resolution]: "Fluture"
+
+> fork (log ('rejection')) (log ('resolution')) (eventualCachedPackageName)
+[resolution]: "Fluture"
```
#### isFuture
-isFuture :: a -> Boolean
-
```hs
isFuture :: a -> Boolean
```
-
-
Returns true for [Futures](#types) and false for everything else. This function
(and [`S.is`][S:is]) also return `true` for instances of Future that were
created within other contexts. It is therefore recommended to use this over
`instanceof`, unless your intent is to explicitly check for Futures created
using the exact `Future` constructor you're testing against.
-
```js
-var Future1 = require('/path/to/fluture');
-var Future2 = require('/other/path/to/fluture');
-var noop = () => {};
+> isFuture (resolve (42))
+true
-var m1 = Future1(noop);
-Future1.isFuture(m1) === (m1 instanceof Future1);
-//> true
-
-var m2 = Future2(noop);
-Future1.isFuture(m2) === (m2 instanceof Future1);
-//> false
+> isFuture (42)
+false
```
#### never
-never :: Future a a
-
```hs
-never :: Future a a
+never :: Future a b
```
-
-
A Future that never settles. Can be useful as an initial value when reducing
with [`race`](#race), for example.
#### isNever
-isNever :: a -> Boolean
-
```hs
isNever :: a -> Boolean
```
-
-
Returns `true` if the given input is a `never`.
#### extractLeft
-extractLeft :: Future a b -> Array a
-
```hs
-extractLeft :: Future a b -> Array a
-Future.prototype.extractLeft :: Future a b ~> () -> Array a
+extractLeft :: Future a b -> Array a
```
-
-
Returns an array whose only element is the rejection reason of the Future.
In many cases it will be impossible to extract this value; In those cases, the
array will be empty. This function is meant to be used for type introspection:
@@ -1756,15 +1419,10 @@ it is **not** the correct way to [consume a Future](#consuming-futures).
#### extractRight
-extractRight :: Future a b -> Array b
-
```hs
-extractRight :: Future a b -> Array b
-Future.prototype.extractRight :: Future a b ~> () -> Array b
+extractRight :: Future a b -> Array b
```
-
-
Returns an array whose only element is the resolution value of the Future.
In many cases it will be impossible to extract this value; In those cases, the
array will be empty. This function is meant to be used for type introspection:
@@ -1772,19 +1430,15 @@ it is **not** the correct way to [consume a Future](#consuming-futures).
#### debugMode
-debugMode :: Boolean -> Undefined
-
```hs
debugMode :: Boolean -> Undefined
```
-
-
Enable or disable Fluture's debug mode. Debug mode is disabled by default.
Pass `true` to enable, or `false` to disable.
```js
-Future.debugMode(true);
+debugMode (true)
```
For more information, see [Debugging](#debugging) and [Context](#context).
@@ -1792,7 +1446,7 @@ For more information, see [Debugging](#debugging) and [Context](#context).
#### context
```hs
-context :: Future a b ~> List Context
+Future.prototype.context :: Future a b ~> List Context
```
A linked list of debugging contexts made available on every instance of
@@ -1800,18 +1454,7 @@ A linked list of debugging contexts made available on every instance of
The context objects have `stack` properties which contain snapshots of the
stacktraces leading up to the creation of the `Future` instance. They are used
-by Fluture to generate asynchronous stack traces.
-
-```js
-Future.debugMode(true);
-const future = Future.after(10, 'Hello');
-
-let context = future.context;
-while(context.head){
- console.log(context.head.stack);
- context = context.tail;
-}
-```
+by Fluture to generate contextual stack traces.
## License
@@ -1856,6 +1499,7 @@ while(context.head){
[Guide:constraints]: https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch7.html#constraints
[1]: https://en.wikipedia.org/wiki/Continuation-passing_style
+[2]: https://github.com/tc39/proposal-pipeline-operator
[3]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterator
[5]: https://vimeo.com/106008027
[6]: https://github.com/rpominov/static-land
diff --git a/package.json b/package.json
index c4dc2917..c989aa0f 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,7 @@
"build": "rollup -c rollup.config.js",
"build:dist": "rollup -c rollup.config.dist.js",
"clean": "rimraf npm-debug.log coverage index.js .esm-cache .nyc_output node_modules/.cache",
- "lint": "eslint --ext mjs src test index.mjs index.cjs.mjs README.md",
+ "lint": "eslint --ext mjs src test index.mjs index.cjs.mjs",
"lint:readme": "remark --no-stdout --frail -u remark-validate-links README.md",
"release": "xyz --edit --repo git@github.com:fluture-js/Fluture.git --tag 'X.Y.Z' --script scripts/distribute --increment",
"test": "npm run lint && npm run lint:readme && npm run test:unit && npm run test:prop && npm run test:integration && npm run test:types && npm run test:build",
@@ -69,7 +69,6 @@
"es-check": "^5.0.0",
"eslint": "^5.5.0",
"eslint-config-warp": "^3.0.0",
- "eslint-plugin-markdown": "^1.0.0-beta.7",
"esm": "^3.0.81",
"fantasy-laws": "^1.0.1",
"jsverify": "^0.8.3",