Repository: Avaq/Fluture Branch: master Commit: 1d925dea0008 Files: 139 Total size: 383.8 KB Directory structure: gitextract_iludrmak/ ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github/ │ ├── FUNDING.yml │ ├── dependabot.yml │ └── workflows/ │ └── test.yml ├── .gitignore ├── .npmrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bench/ │ ├── .eslintrc.json │ ├── .npmrc │ ├── README.md │ ├── fluture.js │ ├── folktale.js │ ├── package.json │ └── promise.js ├── dist/ │ ├── bundle.js │ └── module.js ├── index.cjs.js ├── index.d.ts ├── index.js ├── package.json ├── rollup.config.dist.js ├── rollup.config.js ├── scripts/ │ ├── bench.js │ ├── distribute │ ├── test-esm │ └── test-mem ├── src/ │ ├── after.js │ ├── alt.js │ ├── and.js │ ├── ap.js │ ├── attempt-p.js │ ├── attempt.js │ ├── bichain.js │ ├── bimap.js │ ├── both.js │ ├── cache.js │ ├── chain-rej.js │ ├── chain.js │ ├── coalesce.js │ ├── done.js │ ├── encase-p.js │ ├── encase.js │ ├── extract-left.js │ ├── extract-right.js │ ├── fork-catch.js │ ├── fork.js │ ├── future.js │ ├── go.js │ ├── hook.js │ ├── internal/ │ │ ├── const.js │ │ ├── debug.js │ │ ├── error.js │ │ ├── iteration.js │ │ ├── list.js │ │ ├── parallel.js │ │ ├── predicates.js │ │ ├── timing.js │ │ └── utils.js │ ├── lastly.js │ ├── map-rej.js │ ├── map.js │ ├── node.js │ ├── pap.js │ ├── par.js │ ├── parallel.js │ ├── promise.js │ ├── race.js │ ├── reject-after.js │ ├── seq.js │ ├── swap.js │ └── value.js └── test/ ├── arbitraries.js ├── assertions.js ├── build/ │ ├── main.js │ └── require.js ├── integration/ │ └── main.js ├── prop/ │ ├── 0.algebra.js │ ├── 0.fantasy-land.js │ ├── 1.fantasy-libs.js │ └── 2.arbitrary.js ├── types/ │ └── map.test-d.ts ├── unit/ │ ├── 0.debug.js │ ├── 0.error.js │ ├── 0.iteration.js │ ├── 0.list.js │ ├── 0.predicates.js │ ├── 0.utils.js │ ├── 1.future.js │ ├── 1.is-future.js │ ├── 1.is-never.js │ ├── 1.never.js │ ├── 1.pipe.js │ ├── 2.done.js │ ├── 2.extract-left.js │ ├── 2.extract-right.js │ ├── 2.fork-catch.js │ ├── 2.fork.js │ ├── 2.promise.js │ ├── 2.value.js │ ├── 3.after.js │ ├── 3.attempt-p.js │ ├── 3.attempt.js │ ├── 3.cache.js │ ├── 3.crash.js │ ├── 3.encase-p.js │ ├── 3.encase.js │ ├── 3.go.js │ ├── 3.hook.js │ ├── 3.node.js │ ├── 3.parallel.js │ ├── 3.reject-after.js │ ├── 3.reject.js │ ├── 3.resolve.js │ ├── 4.alt.js │ ├── 4.and.js │ ├── 4.ap.js │ ├── 4.bichain.js │ ├── 4.bimap.js │ ├── 4.both.js │ ├── 4.chain-rej.js │ ├── 4.chain.js │ ├── 4.coalesce.js │ ├── 4.lastly.js │ ├── 4.map-rej.js │ ├── 4.map.js │ ├── 4.pap.js │ ├── 4.race.js │ ├── 4.swap.js │ ├── 5.chain-rec.js │ ├── 5.par.js │ └── 5.seq.js └── util/ ├── futures.js ├── props.js └── util.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true ================================================ FILE: .eslintignore ================================================ /test/build/require.js ================================================ FILE: .eslintrc.json ================================================ { "root": true, "extends": ["warp"], "parserOptions": { "ecmaVersion": 2015, "sourceType": "module" }, "globals": { "setTimeout": false, "clearTimeout": false, "Promise": false }, "rules": { "space-before-blocks": [2, "never"], "consistent-this": [2, "_this"], "semi": [2, "always", {"omitLastInOneLineBlock": true}], "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": ["off"], "no-param-reassign": ["off"], "no-shadow": ["off"], "keyword-spacing": ["off"], "no-plusplus": ["off"], "operator-assignment": ["off"], "one-var": ["off"], "generator-star-spacing": ["error", {"before": false, "after": false}], "no-mixed-operators": ["off"], "capitalized-comments": ["off"], "init-declarations": ["off"], "function-call-argument-newline": ["off"], "no-multi-assign": ["off"], "no-invalid-this": ["off"] }, "overrides": [ { "files": ["test/**"], "env": {"node": true}, "globals": {"Promise": false}, "rules": { "arrow-body-style": ["off"], "no-sequences": ["off"], "max-len": ["off"], "space-before-function-paren": ["error", "always"] } } ] } ================================================ FILE: .github/FUNDING.yml ================================================ github: [Avaq] ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: npm directory: "/" schedule: interval: daily open-pull-requests-limit: 10 reviewers: - Avaq ================================================ FILE: .github/workflows/test.yml ================================================ name: Test on: pull_request: push: branches: - master - 11.x jobs: test: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v1 - name: Install NodeJS uses: actions/setup-node@v1 with: node-version: 18.x - name: Install Dependencies run: npm install - name: Execute Tests run: npm test - name: Upload Coverage Report run: npm run coverage:upload env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} ================================================ FILE: .gitignore ================================================ /bench/node_modules/ /coverage/ /node_modules/ /perf/ /index.cjs ================================================ FILE: .npmrc ================================================ package-lock=false ================================================ FILE: CHANGELOG.md ================================================ # Change Log Please refer to the [release notes](https://github.com/fluture-js/Fluture/releases). ================================================ FILE: CONTRIBUTING.md ================================================ # Contribution guide ## Making a contribution * Fork and clone the project * Commit changes to a branch named after the work that was done * Make sure the tests pass locally * Create a pull request ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2020 Aldwin Vlasblom Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # [![Fluture](logo.png)](#butterfly) [![Build Status][]](https://github.com/fluture-js/Fluture/actions?query=branch%3Amaster+workflow%3ATest) [![Code Coverage][]](https://codecov.io/gh/fluture-js/Fluture/branch/master) [![Dependency Status][]](https://david-dm.org/fluture-js/Fluture) [![NPM Package][]](https://www.npmjs.com/package/fluture) [![Gitter Chat][]](https://gitter.im/fluture-js/Fluture) [Build Status]: https://github.com/fluture-js/Fluture/workflows/Test/badge.svg [Code Coverage]: https://img.shields.io/codecov/c/github/fluture-js/Fluture/master.svg [Dependency Status]: https://img.shields.io/david/fluture-js/Fluture.svg [NPM Package]: https://img.shields.io/npm/v/fluture.svg [Gitter Chat]: https://img.shields.io/gitter/room/fluture-js/Fluture.svg?colorB=blue Fluture offers a control structure similar to Promises, Tasks, Deferreds, and what-have-you. Let's call them Futures. Much like Promises, Futures represent the value arising from the success or failure of an asynchronous operation (I/O). Though unlike Promises, Futures are *lazy* and adhere to [the *monadic* interface](#interoperability). Some of the features provided by Fluture include: * [Cancellation](#cancellation). * [Resource management utilities](#resource-management). * [Stack safe composition and recursion](#stack-safety). * [Integration](#sanctuary) with [Sanctuary][S]. * [A pleasant debugging experience](#debugging). For more information: * [API documentation](#documentation) * [Article: Introduction to Fluture - A Functional Alternative to Promises][10] * [Wiki: Compare Futures to Promises][wiki:promises] * [Wiki: Compare Fluture to similar libraries][wiki:similar] * [Video: Monad a Day - Futures by @DrBoolean][5] ## Installation ### With NPM ```console $ npm install --save fluture ``` ### Bundled from a CDN To load Fluture directly into a browser, a code pen, or [Deno][], use one of the following downloads from the JSDelivr content delivery network. These are single files that come with all of Fluture's dependencies pre-bundled. - [Fluture Script][]: A JavaScript file that adds `Fluture` to the global scope. Ideal for older browsers and code pens. - [Fluture Script Minified][]: The same as above, but minified. - [Fluture Module][]: An EcmaScript module with named exports. Ideal for Deno or modern browsers. - [Fluture Module Minified][]: A minified EcmaScript module without TypeScript typings. Not recommended for Deno. [Fluture Script]: https://cdn.jsdelivr.net/gh/fluture-js/Fluture@14.0.0/dist/bundle.js [Fluture Script Minified]: https://cdn.jsdelivr.net/gh/fluture-js/Fluture@14.0.0/dist/bundle.min.js [Fluture Module]: https://cdn.jsdelivr.net/gh/fluture-js/Fluture@14.0.0/dist/module.js [Fluture Module Minified]: https://cdn.jsdelivr.net/gh/fluture-js/Fluture@14.0.0/dist/module.min.js ## Usage ### EcmaScript Module Fluture is written as modular JavaScript. - On Node 12 and up, Fluture can be loaded directly with `import 'fluture'`. - On some older (minor) Node versions, you may need to import from `'fluture/index.js'` instead, and/or pass `--experimental-modules` to `node`. - On Node versions below 12, the [esm loader][esm] can be used. Alternatively, there is a [CommonJS Module](#commonjs-module) available. - Modern browsers can run Fluture directly. If you'd like to try this out, I recommend installing Fluture with [Pika][] or [Snowpack][]. You can also try the [bundled module](#bundled-from-a-cdn) to avoid a package manager. - For older browsers, use a bundler such as [Rollup][] or WebPack. Besides the module system, Fluture uses purely ES5-compatible syntax, so the source does not have to be transpiled after bundling. Alternatively, there is a [CommonJS Module](#commonjs-module) available. ```js import {readFile} from 'fs' import {node, encase, chain, map, fork} from 'fluture' const getPackageName = file => ( node (done => { readFile (file, 'utf8', done) }) .pipe (chain (encase (JSON.parse))) .pipe (map (x => x.name)) ) getPackageName ('package.json') .pipe (fork (console.error) (console.log)) ``` ### CommonJS Module Although the Fluture source uses the EcmaScript module system, the `main` file points to a CommonJS version of Fluture. On older environments one or more of the following functions may need to be polyfilled: [`Object.create`][JS:Object.create], [`Object.assign`][JS:Object.assign] and [`Array.isArray`][JS:Array.isArray]. ```js const fs = require ('fs') const Future = require ('fluture') 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') .pipe (Future.fork (console.error) (console.log)) ``` ## Documentation ### Table of contents
General - [Installation instructions](#installation) - [Usage instructions](#usage) - [About the Fluture project](#butterfly) - [On interoperability with other libraries](#interoperability) - [How to read the type signatures](#type-signatures) - [How cancellation works](#cancellation) - [On stack safety](#stack-safety) - [Debugging with Fluture](#debugging) - [Casting Futures to String](#casting-futures-to-string) - [Usage with Sanctuary](#sanctuary) - [Using multiple versions of Fluture alongside each other](#incompatible-fluture-versions)
Creating new Futures - [`Future`: Create a possibly cancellable Future](#future) - [`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) - [`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)
Converting between Nodeback APIs and Futures - [`node`: Create a Future using a Node-style callback](#node) - [`done`: Consume a Future by providing a Nodeback](#done)
Converting between Promises and Futures - [`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)
Transforming and combining Futures - [`pipe`: Apply a function to a Future in a fluent method chain](#pipe) - [`map`: Synchronously process the success value in a Future](#map) - [`bimap`: Synchronously process the success or failure value in a Future](#bimap) - [`chain`: Asynchronously process the success value in a Future](#chain) - [`bichain`: Asynchronously process the success or failure value in a Future](#bichain) - [`swap`: Swap the success with the failure value](#swap) - [`mapRej`: Synchronously process the failure value in a Future](#maprej) - [`chainRej`: Asynchronously process the failure value in a Future](#chainrej) - [`coalesce`: Coerce success and failure values into the same success value](#coalesce) - [`ap`: Combine the success values of multiple Futures using a function](#ap) - [`pap`: Combine the success values of multiple Futures in parallel using a function](#pap) - [`and`: Logical *and* for Futures](#and) - [`alt`: Logical *or* for Futures](#alt) - [`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)
Consuming/forking Futures - [`fork`: Standard way to run a Future and get at its result](#fork) - [`forkCatch`: Fork with exception recovery](#forkcatch) - [`value`: Shorter variant of `fork` for Futures sure to succeed](#value) - [`done`: Nodeback style `fork`](#done) - [`promise`: Convert a Future to a Promise](#promise)
Concurrency related utilities and data structures - [`pap`: Combine the success values of multiple Futures in parallel using a function](#pap) - [`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) - [`ConcurrentFuture`: A separate data-type for doing algebraic concurrency](#concurrentfuture) - [`alt`: Behaves like `race` on `ConcurrentFuture` instances](#alt)
Resource management - [`hook`: Safely create and dispose resources](#hook) - [`lastly`: Run a Future after the previous settles](#lastly)
Other utilities - [`pipe`: Apply a function to a Future in a fluent method chain](#pipe) - [`cache`: Cache a Future so that it can be forked multiple times](#cache) - [`isFuture`: Determine whether a value is a Fluture compatible Future](#isfuture) - [`never`: A Future that never settles](#never) - [`debugMode`: Configure Fluture's debug mode](#debugmode) - [`context`: The debugging context of a Future instance](#context)
### Butterfly The name "Fluture" is a conjunction of "FL" (the acronym to [Fantasy Land][FL]) and "future". Fluture means butterfly in Romanian: A creature one might expect to see in Fantasy Land. Credit goes to Erik Fuente for styling the logo, and [WEAREREASONABLEPEOPLE][9] for sponsoring the project. ### Interoperability [Fantasy Land][FL] * `Future` implements [Fantasy Land][FL] 1.0+ -compatible `Alt`, `Bifunctor`, `Monad`, and `ChainRec` (`of`, `ap`, `alt`, `map`, `bimap`, `chain`, `chainRec`). * `Future.Par` implements [Fantasy Land 3][FL] -compatible `Alternative` (`of`, `zero`, `map`, `ap`, `alt`). * The Future and ConcurrentFuture representatives contain `@@type` properties for [Sanctuary Type Identifiers][STI]. * The Future and ConcurrentFuture instances contain `@@show` properties for [Sanctuary Show][SS]. ### Type signatures The various function signatures are provided in a small language referred to as Hindley-Milner notation. In summary, the syntax is as follows: `InputType -> OutputType`. Now, because functions in Fluture are [curried][Guide:currying], the "output" of a function is often *another function*. In Hindley-Milner that's simply written as `InputType -> InputToSecondFunction -> OutputType` and so forth. By convention, types starting with an upper-case letter are [concrete types](#types). When they start with a lower-case letter they're *type variables*. You can think of these type variables as generic types. So `a -> b` denotes a function from generic type `a` to generic type `b`. Finally, through so-called [*constraints*](#type-classes), type variables can be forced to conform to an "interface" (or *Type Class* in functional jargon). For example, `MyInterface a => a -> b`, denotes a function from generic type `a` to generic type `b`, *where `a` must implement `MyInterface`*. You can read in depth about [Hindley-Milner in JavaScript][Guide:HM] here. #### Types The concrete types you will encounter throughout this documentation: - **Future** - Instances of Future provided by [compatible versions](#incompatible-fluture-versions) of Fluture. - **ConcurrentFuture** - Futures wrapped with ([`Future.Par`](#concurrentfuture)). - **Promise a b** - Values which conform to the [Promises/A+ specification][7] and have a rejection reason of type `a` and a resolution value of type `b`. - **Nodeback a b** - A Node-style callback; A function of signature `(a | Nil, b) -> x`. - **Pair a b** - An array with exactly two elements: `[a, b]`. - **Iterator** - Objects with `next`-methods which conform to the [Iterator protocol][3]. - **Cancel** - The nullary [cancellation](#cancellation) functions returned from computations. - **Throwing e a b** - A function from `a` to `b` that may throw an exception `e`. - **List** - Fluture's internal linked-list structure: `{ head :: Any, tail :: List }`. - **Context** - Fluture's internal debugging context object: `{ tag :: String, name :: String, stack :: String }`. #### Type classes Some signatures contain [constrained type variables][Guide:constraints]. Generally, these constraints express that some value must conform to a [Fantasy Land][FL]-specified interface. - **Functor** - [Fantasy Land Functor][FL:functor] conformant values. - **Bifunctor** - [Fantasy Land Bifunctor][FL:bifunctor] conformant values. - **Chain** - [Fantasy Land Chain][FL:chain] conformant values. - **Apply** - [Fantasy Land Apply][FL:apply] conformant values. - **Alt** - [Fantasy Land Alt][FL:alt] conformant values. ### Cancellation Cancellation is a system whereby running Futures get an opportunity to stop what they're doing and release resources that they were holding, when the consumer indicates it is no longer interested in the result. To cancel a Future, it must be unsubscribed from. Most of the [consumption functions](#consuming-futures) return an `unsubscribe` function. Calling it signals that we are no longer interested in the result. After calling `unsubscribe`, Fluture guarantees that our callbacks will not be called; but more importantly: a cancellation signal is sent upstream. The cancellation signal travels all the way back to the source (with the exception of cached Futures - see [`cache`](#cache)), allowing all parties along the way to clean up. With the [`Future` constructor](#future), we can provide a custom cancellation 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. const eventualAnswer = Future (function computeTheAnswer (rej, res) { // We give the computer time to think about the answer, which is 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 () { // Clearing the timeout releases the resources we were holding. clearTimeout (timeoutId) } }) // Now, let's fork our computation and wait for an answer. Forking gives us // the unsubscribe function. 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 () ``` Many natural sources in Fluture have cancellation handlers of their own. [`after`](#after), for example, does exactly what we've done just now: calling `clearTimeout`. Finally, Fluture unsubscribes from Futures that it forks *for us*, when it no longer needs the result. For example, both Futures passed into [race](#race) are forked, but once one of them produces a result, the other is unsubscribed from, triggering cancellation. This means that generally, unsubscription and cancellation is fully managed for us behind the scenes. ### Stack safety Fluture interprets our transformations in a stack safe way. This means that none of the following operations result in a `RangeError: Maximum call stack size exceeded`: ```js > const add1 = x => x + 1 > let m = resolve (1) > for (let i = 0; i < 100000; i++) { . m = map (add1) (m) . } > fork (log ('rejection')) (log ('resolution')) (m) [resolution]: 100001 ``` ```js > const m = (function recur (x) { . const mx = resolve (x + 1) . return x < 100000 ? chain (recur) (mx) : mx . }(1)) > fork (log ('rejection')) (log ('resolution')) (m) [resolution]: 100001 ``` To learn more about memory and stack usage under different types of recursion, see (or execute) [`scripts/test-mem`](scripts/test-mem). ### Debugging First and foremost, Fluture type-checks all of its input and throws TypeErrors when incorrect input is provided. The messages they carry are designed to provide enough insight to figure out what went wrong. Secondly, Fluture catches exceptions that are thrown asynchronously, and exposes them to you in one of two ways: 1. By throwing an Error when it happens. 2. By calling your [exception handler](#forkcatch) with an Error. The original exception isn't used because it might have been any value. Instead, a regular JavaScript Error instance whose properties are based on the original exception is created. Its properties are as follows: - `name`: Always just `"Error"`. - `message`: The original error message, or a message describing the value. - `reason`: The original value that was caught by Fluture. - `context`: A linked list of "context" objects. This is used to create the `stack` property, and you generally don't need to look at it. If debug mode is not enabled, the list is always empty. - `stack`: The stack trace of the original exception if it had one, or the Error's own stack trace otherwise. If debug mode (see below) is enabled, additional stack traces from the steps leading up to the crash are included. - `future`: The instance of [`Future`](#future) that was being [consumed](#consuming-futures) when the exception happened. Often [printing it as a String](#casting-futures-to-string) can yield useful information. You can also try to consume it in isolation to better identify what's going wrong. Finally, as mentioned, Fluture has a [debug mode](#debugmode) wherein additional contextual information across multiple JavaScript ticks is collected, included as an extended "async stack trace" on Errors, and [exposed on Future instances](#context). Debug mode can have a significant impact on performance, and uses up memory, so I would advise against using it in production. ### Casting Futures to String There are multiple ways to print a Future to String. Let's take a simple computation as an example: ```js const add = a => b => a + b; const eventualAnswer = ap (resolve (22)) (map (add) (resolve (20))); ``` 1. Casting it to String directly by calling `String(eventualAnswer)` or `eventualAnswer.toString()` will yield an approximation of the code that was used to create the Future. In this case: ```js "ap (resolve (22)) (map (a => b => a + b) (resolve (20)))" ``` 2. Casting it to String using `JSON.stringify(eventualAnswer, null, 2)` will yield a kind of abstract syntax tree. ```json { "$": "fluture/Future@5", "kind": "interpreter", "type": "transform", "args": [ { "$": "fluture/Future@5", "kind": "interpreter", "type": "resolve", "args": [ 20 ] }, [ { "$": "fluture/Future@5", "kind": "transformation", "type": "ap", "args": [ { "$": "fluture/Future@5", "kind": "interpreter", "type": "resolve", "args": [ 22 ] } ] }, { "$": "fluture/Future@5", "kind": "transformation", "type": "map", "args": [ null ] } ] ] } ``` ### Sanctuary When using this module with [Sanctuary Def][$] (and [Sanctuary][S] by extension) one might run into the following issue: ```js > 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 Fluture to determine whether the type-variables are consistent. 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 > import sanctuary from 'sanctuary' > import {env as flutureEnv} from 'fluture-sanctuary-types' > import {resolve} from 'fluture' > const S = sanctuary.create ({checkTypes: true, env: sanctuary.env.concat (flutureEnv)}) > fork (log ('rejection')) . (log ('resolution')) . (S.I (resolve (42))) [resolution]: 42 ``` ### Incompatible Fluture Versions Most versions of Fluture understand how to consume instances from most other versions, even across Fluture's major releases. This allows for different packages that depend on Fluture to interact. However, sometimes it's unavoidable that a newer version of Fluture is released that can no longer understand older versions, and vice-versa. This only ever happens on a major release, and will be mentioned in the breaking change log. When two incompatible versions of Fluture meet instances, they do their best to issue a clear error message about it. When this happens, you need to manually convert the older instance to a newer instance of Future. When [`isFuture`](#isfuture) returns `false`, a conversion is necessary. You can also apply this trick if the Future comes from another library similar to Fluture. ```js const NoFuture = require ('incompatible-future') const incompatible = NoFuture.of ('Hello') const compatible = Future ((rej, res) => { return NoFuture.fork (rej) (res) (incompatible) }) both (compatible) (resolve ('world')) ``` ### Creating Futures #### Future ```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`. When the computation is finished (possibly asynchronously) it may call the appropriate continuation with a failure or success value. Additionally, the computation must return a nullary function containing cancellation logic. See [Cancellation](#cancellation). If you find that there is no way to cancel your computation, you can return a `noop` function as a cancellation function. However, at this point there is usually a more fitting way to [create that Future](#creating-futures) (like for example via [`node`](#node)). ```js > fork (log ('rejection')) . (log ('resolution')) . (Future (function computation (reject, resolve) { . const t = setTimeout (resolve, 20, 42) . return () => clearTimeout (t) . })) [resolution]: 42 ``` #### resolve ```hs resolve :: b -> Future a b ``` Creates a Future which immediately resolves with the given value. ```js > fork (log ('rejection')) . (log ('resolution')) . (resolve (42)) [answer]: 42 ``` #### reject ```hs reject :: a -> Future a b ``` Creates a Future which immediately rejects with the given value. ```js > fork (log ('rejection')) . (log ('resolution')) . (reject ('It broke!')) [rejection]: "It broke!" ``` #### after ```hs after :: Number -> b -> Future a b ``` Creates a Future which resolves with the given value after the given number of milliseconds. ```js > fork (log ('rejection')) . (log ('resolution')) . (after (20) (42)) [resolution]: 42 ``` #### rejectAfter ```hs rejectAfter :: Number -> a -> Future a b ``` Creates a Future which rejects with the given reason after the given number of milliseconds. ```js > fork (log ('rejection')) . (log ('resolution')) . (rejectAfter (20) ('It broke!')) [rejection]: "It broke!" ``` #### go ```hs 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. ```js > 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!" ``` A rejected Future short-circuits the whole coroutine. ```js > 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!" ``` To handle rejections *inside* the coroutine, we need to [`coalesce`](#coalesce) the error into our control domain. I recommend using coalesce with an [`Either`][S:Either]. ```js > const control = coalesce (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!" ``` #### attempt ```hs attempt :: Throwing e Undefined 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 [`encase (f) (undefined)`](#encase). ```js > const data = {foo: 'bar'} > fork (log ('rejection')) . (log ('resolution')) . (attempt (() => data.foo.bar.baz)) [rejection]: new TypeError ("Cannot read property 'baz' of undefined") ``` #### attemptP ```hs attemptP :: (Undefined -> Promise a b) -> Future a b ``` 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 [`encaseP (f) (undefined)`](#encasep). ```js > fork (log ('rejection')) . (log ('resolution')) . (attemptP (() => Promise.resolve (42))) [resolution]: 42 ``` #### node ```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**. ```js > fork (log ('rejection')) . (log ('resolution')) . (node (done => done (null, 42))) [resolution]: 42 ``` #### encase ```hs encase :: Throwing 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. Applying `encase` with a function `f` creates a "safe" version of `f`. Instead of throwing exceptions, the encased version always returns a Future. ```js > fork (log ('rejection')) . (log ('resolution')) . (encase (JSON.parse) ('{"foo" = "bar"}')) [rejection]: new SyntaxError ('Unexpected token =') ``` #### encaseP ```hs encaseP :: (a -> Promise e r) -> a -> Future e r ``` 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. ```js > 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" ``` ### Transforming Futures #### map ```hs map :: Functor m => (a -> b) -> m a -> m b ``` 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 > fork (log ('rejection')) . (log ('resolution')) . (map (x => x + 1) (resolve (41))) [resolution]: 42 ``` For comparison, an approximation with Promises is: ```js > Promise.resolve (41) . .then (x => x + 1) . .then (log ('resolution'), log ('rejection')) [resolution]: 42 ``` #### bimap ```hs bimap :: Bifunctor m => (a -> c) -> (b -> d) -> m a b -> m c d ``` 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 > fork (log ('rejection')) . (log ('resolution')) . (bimap (x => x + '!') (x => x + 1) (resolve (41))) [resolution]: 42 > fork (log ('rejection')) . (log ('resolution')) . (bimap (x => x + '!') (x => x + 1) (reject ('It broke!'))) [rejection]: "It broke!!" ``` For comparison, an approximation with Promises is: ```js > Promise.resolve (41) . .then (x => x + 1, x => Promise.reject (x + '!')) . .then (log ('resolution'), log ('rejection')) [resolution]: 42 > Promise.reject ('It broke!') . .then (x => x + 1, x => Promise.reject (x + '!')) . .then (log ('resolution'), log ('rejection')) [rejection]: "It broke!!" ``` #### chain ```hs chain :: Chain m => (a -> m b) -> m a -> m b ``` 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. See also [`chainRej`](#chainrej). ```js > fork (log ('rejection')) . (log ('resolution')) . (chain (x => resolve (x + 1)) (resolve (41))) [resolution]: 42 ``` For comparison, an approximation with Promises is: ```js > Promise.resolve (41) . .then (x => Promise.resolve (x + 1)) . .then (log ('resolution'), log ('rejection')) [resolution]: 42 ``` #### bichain ```hs bichain :: (a -> Future c d) -> (b -> Future c d) -> Future a b -> Future c d ``` Sequence a new Future using either the resolution or the rejection value from another. Similarly to [`bimap`](#bimap), `bichain` expects two functions. But instead of returning the new *value*, bichain expects Futures to be returned. ```js > fork (log ('rejection')) . (log ('resolution')) . (bichain (resolve) (x => resolve (x + 1)) (resolve (41))) [resolution]: 42 > fork (log ('rejection')) . (log ('resolution')) . (bichain (x => resolve (x + 1)) (resolve) (reject (41))) [resolution]: 42 ``` For comparison, an approximation with Promises is: ```js > Promise.resolve (41) . .then (x => Promise.resolve (x + 1), Promise.resolve) . .then (log ('resolution'), log ('rejection')) [resolution]: 42 > Promise.reject (41) . .then (Promise.resolve, x => Promise.resolve (x + 1)) . .then (log ('resolution'), log ('rejection')) [resolution]: 42 ``` #### swap ```hs swap :: Future a b -> Future b a ``` Swap the rejection and resolution branches. ```js > fork (log ('rejection')) . (log ('resolution')) . (swap (resolve (42))) [rejection]: 42 > fork (log ('rejection')) . (log ('resolution')) . (swap (reject (42))) [resolution]: 42 ``` #### mapRej ```hs 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 > fork (log ('rejection')) . (log ('resolution')) . (mapRej (s => `Oh no! ${s}`) (reject ('It broke!'))) [rejection]: "Oh no! It broke!" ``` For comparison, an approximation with Promises is: ```js > Promise.reject ('It broke!') . .then (null, s => Promise.reject (`Oh no! ${s}`)) . .then (log ('resolution'), log ('rejection')) [rejection]: "Oh no! It broke!" ``` #### chainRej ```hs 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 > fork (log ('rejection')) . (log ('resolution')) . (chainRej (s => resolve (`${s} But it's all good.`)) (reject ('It broke!'))) [resolution]: "It broke! But it's all good." ``` For comparison, an approximation with Promises is: ```js > 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." ``` #### coalesce ```hs coalesce :: (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. This provides a convenient means to ensure a Future is always resolved. It can be used with other type constructors, like [`S.Either`][S:Either], to maintain a representation of failure. ```js > fork (log ('rejection')) . (log ('resolution')) . (coalesce (S.Left) (S.Right) (resolve ('hello')) [resolution]: Right ("hello") > fork (log ('rejection')) . (log ('resolution')) . (coalesce (S.Left) (S.Right) (reject ('It broke!')) [resolution]: Left ("It broke!") ``` For comparison, an approximation with Promises is: ```js > Promise.resolve ('hello') . .then (S.Right, S.Left) . .then (log ('resolution'), log ('rejection')) [resolution]: Right ("hello") > Promise.reject ('It broke!') . .then (S.Right, S.Left) . .then (log ('resolution'), log ('rejection')) [resolution]: Left ("It broke!") ``` ### Combining Futures #### ap ```hs 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*. \* Have a look at [`pap`](#pap) for an `ap` function that runs its arguments in parallel. If you must use `ap` (because you're creating a generalized function), but still want Futures passed into it to run in parallel, then you could use [ConcurrentFuture](#concurrentfuture) instead. ```js > fork (log ('rejection')) . (log ('resolution')) . (ap (resolve (7)) (ap (resolve (49)) (resolve (x => y => x - y)))) [resolution]: 42 ``` #### pap ```hs pap :: Future a b -> Future a (b -> c) -> Future a c ``` Has the same signature and function as [`ap`](#ap), but runs the two Futures given to it in parallel. See also [ConcurrentFuture](#concurrentfuture) for a more general way to achieve this. ```js > fork (log ('rejection')) . (log ('resolution')) . (pap (resolve (7)) (pap (resolve (49)) (resolve (x => y => x - y)))) [resolution]: 42 ``` #### alt ```hs alt :: Alt f => f a -> f a -> f a ``` 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. 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). \* 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 > fork (log ('rejection')) . (log ('resolution')) . (alt (resolve ('left')) (resolve ('right'))) [resolution]: "right" > fork (log ('rejection')) . (log ('resolution')) . (alt (resolve ('left')) (reject ('It broke!'))) [resolution]: "left" ``` #### and ```hs and :: Future a c -> Future a b -> Future a c ``` Logical *and* for Futures. 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 [`alt`](#alt) and [`lastly`](#lastly). ```js > fork (log ('rejection')) . (log ('resolution')) . (and (resolve ('left')) (resolve ('right'))) [resolution]: "left" > fork (log ('rejection')) . (log ('resolution')) . (and (resolve ('left')) (reject ('It broke!'))) [rejection]: "It broke!" ``` #### lastly ```hs 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. 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. See also [`and`](#and) and [`alt`](#alt). ```js > fork (log ('rejection')) . (log ('resolution')) . (lastly (encase (log ('lastly')) ('All done!')) (resolve (42))) [lastly]: "All done!" [resolution]: 42 ``` ### Consuming Futures #### fork ```hs 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. This function is called `fork` because it literally represents a fork in our program: a point where a single code-path splits in two. It is recommended to keep the number of calls to `fork` at a minimum for this reason. The more forks, the higher the code complexity. Generally, one only needs to call `fork` in a single place in the entire 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 [Cancellation](#cancellation). 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). Almost all code examples in Fluture use `fork` to run the computation. There are some variations on `fork` that serve different purposes below. #### forkCatch ```hs 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. The exception handler will always be called with an instance of `Error`, independent of what caused the crash. **Using this function is a trade-off;** Generally it's best to let a program crash and restart when an a fatal error occurs. Restarting is the surest way to restore the memory that was allocated by the program to an expected state. By using `forkCatch`, we can keep our program alive after a fatal error, which can be very beneficial when the program is being used by multiple clients. However, since fatal errors might indicate that something, somewhere has entered an invalid state, it's probably still best to restart our program upon encountering one. See [Debugging](#debugging) for information about the Error object that is passed to your exception handler. ```js > 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 ```hs value :: (b -> Any) -> Future a b -> Cancel ``` 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 [`coalesce`](#coalesce). If the Future rejects, `value` will throw an Error. As with [`fork`](#fork), `value` returns an `unsubscribe` function. See [Cancellation](#cancellation). ```js > value (log ('resolution')) (resolve (42)) [resolution]: 42 ``` #### done ```hs done :: Nodeback a b -> Future a b -> Cancel ``` 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. As with [`fork`](#fork), `done` returns an `unsubscribe` function. See [Cancellation](#cancellation). ```js > done ((err, val) => log ('resolution') (val)) (resolve (42)) [resolution]: 42 ``` #### promise ```hs promise :: Future Error a -> Promise Error a ``` Run the Future and get a Promise to represent its continuation. Returns a Promise which resolves with the resolution value, or rejects with the rejection reason of the Future. If an exception was encountered during the computation, the promise will reject with it. I recommend using [`coalesce`](#coalesce) before `promise` to ensure that exceptions and rejections are not mixed into the Promise rejection branch. Cancellation capabilities are lost when using `promise` to consume the Future. ```js > promise (resolve (42)) .then (log ('resolution')) [resolution]: 42 > promise (reject ('failure')) .then (log ('resolution'), log ('rejection')) [rejection]: "failure" ``` ### Parallelism #### race ```hs 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 > fork (log ('rejection')) . (log ('resolution')) . (race (after (15) ('left')) (after (30) ('right'))) [resolution]: "left" ``` #### both ```hs 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 > fork (log ('rejection')) . (log ('resolution')) . (both (after (15) ('left')) (after (30) ('right'))) [resolution]: ["left", "right"] ``` #### parallel ```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. 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. If we use `1` for the limit, the Futures would run in sequence, causing the result to appear only after 200ms. 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. ```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] ``` 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 [coalesce](#coalesce). ```js > fork (log ('rejection')) . (log ('resolution')) . (parallel (2) ([resolve (42), reject ('It broke!')] . .map (coalesce (S.Left) (S.Right)))) [resolution]: [Right (42), Left ("It broke!")] ``` #### ConcurrentFuture The `ConcurrentFuture` type is very similar to the `Future` type, except that it has *parallel* semantics where `Future` has *sequential* semantics. These sematics are most notable in the implementation of Applicative for `ConcurrentFuture`. When using [`ap`](#ap) on two ConcurrentFutures, they run parallely, whereas regular `Future` instances would've run sequentially. This means that `ConcurrentFuture` cannot be a Monad, which is why we have it as a separate type. The implementation of Alternative on `ConcurrentFuture` has parallel semantics as well. Whereas [`alt`](#alt) on regular Futures uses the failure effect to determine a winner, on ConcurrentFutures *timing* is used, and the winner will be whichever ConcurrentFuture settled first. The idea is that we can switch back and forth between `Future` and `ConcurrentFuture`, using [`Par`](#par) and [`seq`](#seq), to get sequential or 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 //Some dummy values const x = 41; const f = a => a + 1; //The following two are equal ways to construct a ConcurrentFuture const parx = S.of (Par) (x) const parf = Par (S.of (Future) (f)) //We can make use of parallel apply value (log ('resolution')) (seq (ap (parx) (parf))) [resolution]: 42 //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 ```hs Par :: Future a b -> ConcurrentFuture a b ``` Converts a Future to a ConcurrentFuture. ##### seq Converts a ConcurrentFuture to a Future. ```hs seq :: ConcurrentFuture a b -> Future a b ``` ### Resource management Functions listed under this category allow for more fine-grained control over the flow of acquired values. #### hook ```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: - `acquire` is a Future which might create connections, open files, etc. - `dispose` is a function that takes the result from `acquire` and should be used to clean up (close connections etc). The Future it returns must resolve, and its resolution value is ignored. If it rejects, a fatal error is raised which can only be handled with [`forkCatch`](#forkcatch). - `consume` is another Function takes the result from `acquire`, and may be used to perform any arbitrary computations using the resource. Typically, you'd want to partially apply this function with the first two arguments (acquisition and disposal), as shown in the example. ```js > import {open, read, close} from 'fs' > 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 will happen. When it's cancelled after acquistion completes, however, the disposal will still run, and if it fails, an exception will be thrown. If you have multiple resources that you'd like to consume all at once, you can use [Fluture Hooks](https://github.com/fluture-js/fluture-hooks) to combine multiple hooks into one. ### Utility functions #### pipe ```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. You can think of this as a fallback for the [ESNext pipe operator (`|>`)][2]. ```js > 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 ```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. This essentially turns a unicast Future into a multicast Future, allowing 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. ```js > 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" > fork (log ('rejection')) (log ('resolution')) (eventualPackageName) [debug]: "Read, parsed, and traversed the package data" [resolution]: "Fluture" > 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 ```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 > isFuture (resolve (42)) true > isFuture (42) false ``` #### never ```hs never :: Future a b ``` A Future that never settles. Can be useful as an initial value when reducing with [`race`](#race), for example. #### isNever ```hs isNever :: a -> Boolean ``` Returns `true` if the given input is a `never`. #### extractLeft ```hs 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: it is **not** the correct way to [consume a Future](#consuming-futures). #### extractRight ```hs 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: it is **not** the correct way to [consume a Future](#consuming-futures). #### debugMode ```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 debugMode (true) ``` For more information, see [Debugging](#debugging) and [Context](#context). #### context ```hs Future.prototype.context :: Future a b ~> List Context ``` A linked list of debugging contexts made available on every instance of `Future`. When [debug mode](#debugmode) is disabled, the list is always empty. 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 contextual stack traces. ## License [MIT licensed](LICENSE) [wiki:similar]: https://github.com/fluture-js/Fluture/wiki/Comparison-of-Future-Implementations [wiki:promises]: https://github.com/fluture-js/Fluture/wiki/Comparison-to-Promises [FL]: https://github.com/fantasyland/fantasy-land [FL:alt]: https://github.com/fantasyland/fantasy-land#alt [FL:alternative]: https://github.com/fantasyland/fantasy-land#alternative [FL:functor]: https://github.com/fantasyland/fantasy-land#functor [FL:chain]: https://github.com/fantasyland/fantasy-land#chain [FL:apply]: https://github.com/fantasyland/fantasy-land#apply [FL:bifunctor]: https://github.com/fantasyland/fantasy-land#bifunctor [FL:chainrec]: https://github.com/fantasyland/fantasy-land#chainrec [JS:Object.create]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create [JS:Object.assign]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign [JS:Array.isArray]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray [S]: https://sanctuary.js.org/ [S:Either]: https://sanctuary.js.org/#either-type [S:is]: https://sanctuary.js.org/#is [S:create]: https://sanctuary.js.org/#create [S:join]: https://sanctuary.js.org/#join [SS]: https://github.com/sanctuary-js/sanctuary-show [STI]: https://github.com/sanctuary-js/sanctuary-type-identifiers [FST]: https://github.com/fluture-js/fluture-sanctuary-types [$]: https://github.com/sanctuary-js/sanctuary-def [Rollup]: https://rollupjs.org/ [Pika]: https://www.pikapkg.com/ [Snowpack]: https://www.snowpack.dev/ [esm]: https://github.com/standard-things/esm [Deno]: https://deno.land/ [Guide:HM]: https://mostly-adequate.gitbook.io/mostly-adequate-guide/ch07 [Guide:constraints]: https://mostly-adequate.gitbook.io/mostly-adequate-guide/ch07#constraints [Guide:currying]: https://mostly-adequate.gitbook.io/mostly-adequate-guide/ch04 [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 [7]: https://promisesaplus.com/ [9]: https://wearereasonablepeople.nl/ [10]: https://dev.to/avaq/fluture-a-functional-alternative-to-promises-21b ================================================ FILE: bench/.eslintrc.json ================================================ { "root": true, "extends": ["warp/node", "warp/es6"], "rules": { "max-len": [0] } } ================================================ FILE: bench/.npmrc ================================================ package-lock=false save=false ================================================ FILE: bench/README.md ================================================ # Benchmarks ## Running * Install node modules required for the benchmark you wish to run * Use `npm run bench -- --help` for options For example, let's say you like to know how the performance of `parallel` compares to what it was at version 5.x.x: ```console $ (cd bench && npm install fluture@5.x.x) $ npm run bench -- --benchmark fluture --match *.parallel.* ``` ================================================ FILE: bench/fluture.js ================================================ /* global setImmediate */ import bench from 'sanctuary-benchmark'; import Old from 'fluture'; import * as New from '../index.js'; const config = {leftHeader: 'Old', rightHeader: 'New'}; const noop = () => {}; const compose = (f, g) => x => f(g(x)); const run = m => m.constructor.fork(noop)(noop)(m); const plus1 = x => x + 1; const arr = (T, length) => Array.from({length}, (_, i) => T.resolve(i)); const fast = (T, x) => T((rej, res) => void setImmediate(res, x)); const slow = (T, x) => T.after(1)(x); export default bench(Old, New, config, { 'def.interpreter.Future': [ {}, ({Future}) => Future((rej, res) => res(1)) ], 'def.interpreter.resolve': [ {}, ({resolve}) => resolve(1) ], 'def.interpreter.reject': [ {}, ({reject}) => reject(1) ], 'def.interpreter.after': [ {}, ({after}) => after(1)(1) ], 'def.interpreter.attempt': [ {}, ({attempt}) => attempt(noop) ], 'def.interpreter.cache': [ {}, ({resolve, cache}) => cache(resolve(1)) ], 'def.interpreter.encase': [ {}, ({encase}) => encase(noop)(1) ], 'def.interpreter.encaseP': [ {}, ({encaseP}) => encaseP(noop)(1) ], 'def.interpreter.go': [ {}, ({go}) => go(noop) ], 'def.interpreter.hook': [ {}, ({resolve, hook}) => hook(resolve(1))(noop)(noop) ], 'def.interpreter.node': [ {}, ({node}) => node(done => done(null, 1)) ], 'def.interpreter.parallel': [ {}, ({resolve, parallel}) => parallel(1)([resolve(1)]) ], 'def.transformation.map': [ {}, ({resolve, map}) => map(plus1)(resolve(1)) ], 'def.transformation.chain': [ {}, ({resolve, chain}) => chain(plus1)(resolve(1)) ], 'run.interpreter.Future': [ {}, ({Future}) => run(Future((rej, res) => res(1))) ], 'run.interpreter.parallel.empty': [ {}, ({parallel}) => run(parallel(1)([])) ], 'run.interpreter.parallel.small.sequential': [ {}, ({Future, parallel}) => run(parallel(1)(arr(Future, 2))) ], 'run.interpreter.parallel.small.concurrent': [ {}, ({Future, parallel}) => run(parallel(2)(arr(Future, 2))) ], 'run.interpreter.parallel.big.sequential': [ {}, ({Future, parallel}) => run(parallel(1)(arr(Future, 100))) ], 'run.interpreter.parallel.big.concurrent': [ {}, ({Future, parallel}) => run(parallel(2)(arr(Future, 100))) ], 'run.interpreter.go': [ {}, ({go, resolve}) => run(go(function*(){ return (yield resolve(1)) + (yield resolve(2)); })) ], 'run.transformation.sync.map': [ {}, ({resolve, map}) => run(map(plus1)(resolve(1))) ], 'run.transformation.sync.swap.one': [ {}, ({resolve, swap}) => run(swap(resolve(42))) ], 'run.transformation.sync.swap.many': [ {}, ({resolve, swap}) => { let m = resolve(1); for(let i = 0; i < 1000; i++){ m = swap(m) } run(m); } ], 'run.transformation.sync.chain.one': [ {}, ({resolve, chain}) => run(chain(compose(resolve, plus1))(resolve(1))) ], 'run.transformation.sync.chain.many': [ {}, ({chain, resolve}) => { const f = compose(resolve, plus1); let m = resolve(1); for(let i = 0; i < 1000; i++){ m = chain(f)(m) } run(m); } ], 'run.transformation.async.map': [ {defer: true}, ({Future, map, value}, [d]) => { map(plus1)(fast(Future, 1)).pipe(value(() => d.resolve())); } ], 'run.transformation.async.chain.one': [ {defer: true}, ({Future, chain, value}, [d]) => { chain(x => fast(Future, plus1(x)))(fast(Future, 1)) .pipe(value(() => d.resolve())); } ], 'run.transformation.async.chain.many': [ {defer: true}, ({Future, chain, value}, [d]) => { const f = x => fast(Future, plus1(x)); let m = fast(Future, 1); for(let i = 0; i < 100; i++){ m = chain(f)(m) } m.pipe(value(() => d.resolve())); } ], 'run.transformation.parallel.async.race.fast-vs-slow': [ {defer: true}, ({Future, race, value}, [d]) => { const a = fast(Future, 1); const b = slow(Future, 1); race(a)(b).pipe(value(() => d.resolve())); } ], 'run.transformation.parallel.async.race.slow-vs-fast': [ {defer: true}, ({Future, race, value}, [d]) => { const a = slow(Future, 1); const b = fast(Future, 1); race(a)(b).pipe(value(() => d.resolve())); } ], 'run.transformation.parallel.async.race.slow-vs-slow': [ {defer: true}, ({Future, race, value}, [d]) => { const a = slow(Future, 1); const b = slow(Future, 1); race(a)(b).pipe(value(() => d.resolve())); } ], }); ================================================ FILE: bench/folktale.js ================================================ import bench from 'sanctuary-benchmark'; import * as Future from '../index.js'; import folktale from 'folktale/concurrency/task/index.js'; const noop = () => {}; const plus1 = x => x + 1; const repeat = (n, f) => x => Array.from({length: n}).reduce(x => f(x), x); const map1000 = repeat(1000, Future.map(plus1)); const chain1000 = repeat(1000, Future.chain(plus1)); const createTask = x => folktale.task(resolver => resolver.resolve(x)); const createFuture = x => Future.Future((rej, res) => { res(x); return noop }); const consumeTask = m => m.run().listen({onCancelled: noop, onRejected: noop, onResolved: noop}); const consumeFuture = Future.fork(noop)(noop); const config = {leftHeader: 'Folktale', rightHeader: 'Fluture'}; const left = { create: createTask, consume: consumeTask, one: createTask(1), mapped: map1000(createTask(1)) }; const right = { create: createFuture, consume: consumeFuture, one: createFuture(1), mapped: map1000(createFuture(1)) }; export default bench(left, right, config, { 'create.construct': [ {}, ({create}) => repeat(1000, create)(1) ], 'create.map': [ {}, ({one}) => map1000(one) ], 'create.chain': [ {}, ({one}) => chain1000(one) ], 'consume.noop': [ {}, ({one, consume}) => consume(one) ], 'consume.map.1': [ {}, ({one, consume}) => consume(Future.map(plus1)(one)) ], 'consume.map.1000': [ {}, ({mapped, consume}) => consume(mapped) ], 'consume.chain': [ {}, ({create, consume, one}) => consume(Future.chain(x => create(x + 1))(one)) ], }); ================================================ FILE: bench/package.json ================================================ { "name": "fluture-benchmarks", "type": "module", "version": "1.0.0", "description": "A package.json for the purpose of being able to install dependencies in here", "author": "Aldwin Vlasblom (https://github.com/Avaq)", "license": "MIT", "private": true } ================================================ FILE: bench/promise.js ================================================ import bench from 'sanctuary-benchmark'; import * as Future from '../index.js'; const plus1 = x => x + 1; const repeat = (n, f, x) => Array.from({length: n}).reduce(x => f(x), x); const Left = x => ({left: true, right: false, value: x}); const Right = x => ({left: false, right: true, value: x}); const config = {leftHeader: 'Promise', rightHeader: 'Fluture'}; const def = f => [ {defer: true}, (x, [d]) => f(x).then(() => d.resolve()), (x, [d]) => Future.value(() => d.resolve())(f(x)) ]; const PromiseInterop = { resolve: x => Promise.resolve(x), map: f => p => p.then(f), chain: f => p => p.then(f), coalesce: f => g => p => p.then(g, f), race: a => b => Promise.race([a, b]), after: n => x => new Promise(res => setTimeout(res, n, x)) }; export default bench(PromiseInterop, Future, config, { 'resolve': def( ({resolve}) => repeat(1000, resolve, 1) ), 'after': def( ({after}) => after(10)(1) ), 'map': def( ({map, resolve}) => repeat(1000, map(plus1), resolve(1)) ), 'coalesce': def( ({coalesce, resolve}) => repeat(1000, coalesce(Left)(Right), resolve(1)) ), 'chain.sync': def( ({chain, resolve}) => repeat(1000, chain(x => resolve(plus1(x))), resolve(1)) ), 'chain.async': def( ({chain, after}) => repeat(5, chain(x => after(1)(plus1(x))), after(1)(1)) ), 'race.sync': def( ({race, resolve}) => repeat(1000, race(resolve(2)), resolve(1)) ), 'race.async': def( ({race, after}) => repeat(5, race(after(1)(2)), after(1)(1)) ), }); ================================================ FILE: dist/bundle.js ================================================ /** * Fluture bundled; version 14.0.0 */ var Fluture = (function () { 'use strict'; function createCommonjsModule(fn) { var module = { exports: {} }; return fn(module, module.exports), module.exports; } /* @@@@@@@ @@@@@@@ @@ @@ @@ @@ @@ @@@ @@ @@@ @@ @@ @@ @@@ @@ @@ @@@@@@ @@ @@@ @@ @@@ @@@@ @@ @@ @@@ @@ @@ @@ @@@ @@ @@@ @@ @@@ @@@ @@ @@@ @@ @@ @@ @@@ @@ @@ @@ @@@ @@ @@@ @@ @@@ @@@ @@ @@@@@@@@ @@ @@ @@@ @@ @@ @@ @@@ @@ @@@ @@ @@@ @@@ @@ @@@ @@ @@@ @@@@@ @@ @@@ @@@@@ @@@ @@@ @@ @@@@@@ @@@@@ @@ @@ @@ @@ @@@@@@@ @@@@@@@ @@@@@ @@ */ var sanctuaryTypeIdentifiers = createCommonjsModule(function (module) { //. # sanctuary-type-identifiers //. //. A type is a set of values. Boolean, for example, is the type comprising //. `true` and `false`. A value may be a member of multiple types (`42` is a //. member of Number, PositiveNumber, Integer, and many other types). //. //. In certain situations it is useful to divide JavaScript values into //. non-overlapping types. The language provides two constructs for this //. purpose: the [`typeof`][1] operator and [`Object.prototype.toString`][2]. //. Each has pros and cons, but neither supports user-defined types. //. //. sanctuary-type-identifiers comprises: //. //. - an npm and browser -compatible package for deriving the //. _type identifier_ of a JavaScript value; and //. - a specification which authors may follow to specify type //. identifiers for their types. //. //. ### Specification //. //. For a type to be compatible with the algorithm: //. //. - every member of the type MUST have a `@@type` property //. (the _type identifier_); and //. //. - the type identifier MUST be a string primitive and SHOULD have //. format `'/[@]'`, where: //. //. - `` MUST consist of one or more characters, and //. SHOULD equal the name of the npm package which defines the //. type (including [scope][3] where appropriate); //. //. - `` MUST consist of one or more characters, and SHOULD //. be the unique name of the type; and //. //. - `` MUST consist of one or more digits, and SHOULD //. represent the version of the type. //. //. If the type identifier does not conform to the format specified above, //. it is assumed that the entire string represents the _name_ of the type; //. _namespace_ will be `null` and _version_ will be `0`. //. //. If the _version_ is not given, it is assumed to be `0`. (function(f) { /* istanbul ignore else */ { module.exports = f (); } } (function() { // $$type :: String var $$type = '@@type'; // pattern :: RegExp var pattern = new RegExp ( '^' + '([\\s\\S]+)' // + '/' // SOLIDUS (U+002F) + '([\\s\\S]+?)' // + '(?:' // optional non-capturing group { + '@' // COMMERCIAL AT (U+0040) + '([0-9]+)' // + ')?' // } + '$' ); //. ### Usage //. //. ```javascript //. const type = require ('sanctuary-type-identifiers'); //. ``` //. //. ```javascript //. > const Identity$prototype = { //. . '@@type': 'my-package/Identity@1', //. . '@@show': function() { //. . return 'Identity (' + show (this.value) + ')'; //. . } //. . } //. //. > const Identity = value => //. . Object.assign (Object.create (Identity$prototype), {value}) //. //. > type (Identity (0)) //. 'my-package/Identity@1' //. //. > type.parse (type (Identity (0))) //. {namespace: 'my-package', name: 'Identity', version: 1} //. ``` //. //. ### API //. //# type :: Any -> String //. //. Takes any value and returns a string which identifies its type. If the //. value conforms to the [specification][4], the custom type identifier is //. returned. //. //. ```javascript //. > type (null) //. 'Null' //. //. > type (true) //. 'Boolean' //. //. > type (Identity (0)) //. 'my-package/Identity@1' //. ``` function type(x) { return x != null && x.constructor != null && x.constructor.prototype !== x && typeof x[$$type] === 'string' ? x[$$type] : (Object.prototype.toString.call (x)).slice ('[object '.length, -']'.length); } //# type.parse :: String -> { namespace :: Nullable String, name :: String, version :: Number } //. //. Takes any string and parses it according to the [specification][4], //. returning an object with `namespace`, `name`, and `version` fields. //. //. ```javascript //. > type.parse ('my-package/List@2') //. {namespace: 'my-package', name: 'List', version: 2} //. //. > type.parse ('nonsense!') //. {namespace: null, name: 'nonsense!', version: 0} //. //. > type.parse (type (Identity (0))) //. {namespace: 'my-package', name: 'Identity', version: 1} //. ``` type.parse = function parse(s) { var namespace = null; var name = s; var version = 0; var groups = pattern.exec (s); if (groups != null) { namespace = groups[1]; name = groups[2]; if (groups[3] != null) version = Number (groups[3]); } return {namespace: namespace, name: name, version: version}; }; return type; })); //. [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof //. [2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString //. [3]: https://docs.npmjs.com/misc/scope //. [4]: #specification }); var FL = { alt: 'fantasy-land/alt', ap: 'fantasy-land/ap', bimap: 'fantasy-land/bimap', chain: 'fantasy-land/chain', chainRec: 'fantasy-land/chainRec', map: 'fantasy-land/map', of: 'fantasy-land/of', zero: 'fantasy-land/zero' }; var ordinal = ['first', 'second', 'third', 'fourth', 'fifth']; var namespace = 'fluture'; var name = 'Future'; var version = 5; var $$type$1 = namespace + '/' + name + '@' + version; function List(head, tail){ this.head = head; this.tail = tail; } List.prototype.toJSON = function(){ return toArray(this); }; var nil = new List(null, null); nil.tail = nil; function isNil(list){ return list.tail === list; } // cons :: (a, List a) -> List a // -- O(1) append operation function cons(head, tail){ return new List(head, tail); } // reverse :: List a -> List a // -- O(n) list reversal function reverse(xs){ var ys = nil, tail = xs; while(!isNil(tail)){ ys = cons(tail.head, ys); tail = tail.tail; } return ys; } // cat :: (List a, List a) -> List a // -- O(n) list concatenation function cat(xs, ys){ var zs = ys, tail = reverse(xs); while(!isNil(tail)){ zs = cons(tail.head, zs); tail = tail.tail; } return zs; } // toArray :: List a -> Array a // -- O(n) list to Array function toArray(xs){ var tail = xs, arr = []; while(!isNil(tail)){ arr.push(tail.head); tail = tail.tail; } return arr; } /* c8 ignore next */ var captureStackTrace = Error.captureStackTrace || captureStackTraceFallback; var _debug = debugHandleNone; function debugMode(debug){ _debug = debug ? debugHandleAll : debugHandleNone; } function debugHandleNone(x){ return x; } function debugHandleAll(x, fn, a, b, c){ return fn(a, b, c); } function debug(x, fn, a, b, c){ return _debug(x, fn, a, b, c); } function captureContext(previous, tag, fn){ return debug(previous, debugCaptureContext, previous, tag, fn); } function debugCaptureContext(previous, tag, fn){ var context = {tag: tag, name: ' from ' + tag + ':'}; captureStackTrace(context, fn); return cons(context, previous); } function captureApplicationContext(context, n, f){ return debug(context, debugCaptureApplicationContext, context, n, f); } function debugCaptureApplicationContext(context, n, f){ return debugCaptureContext(context, ordinal[n - 1] + ' application of ' + f.name, f); } function captureStackTraceFallback(x){ var e = new Error; if(typeof e.stack === 'string'){ x.stack = x.name + '\n' + e.stack.split('\n').slice(1).join('\n'); /* c8 ignore next 3 */ }else { x.stack = x.name; } } var sanctuaryShow = createCommonjsModule(function (module) { //. # sanctuary-show //. //. Haskell has a `show` function which can be applied to a compatible value to //. produce a descriptive string representation of that value. The idea is that //. the string representation should, if possible, be an expression which would //. produce the original value if evaluated. //. //. This library provides a similar [`show`](#show) function. //. //. In general, this property should hold: `eval (show (x)) = x`. In some cases //. parens are necessary to ensure correct interpretation (`{}`, for example, //. is an empty block rather than an empty object in some contexts). Thus the //. property is more accurately stated `eval ('(' + show (x) + ')') = x`. //. //. One can make values of a custom type compatible with [`show`](#show) by //. defining a `@@show` method. For example: //. //. ```javascript //. //# Maybe#@@show :: Maybe a ~> () -> String //. //. //. //. ```javascript //. //. > show (Nothing) //. //. 'Nothing' //. //. //. //. > show (Just (['foo', 'bar', 'baz'])) //. //. 'Just (["foo", "bar", "baz"])' //. //. ``` //. Maybe.prototype['@@show'] = function() { //. return this.isNothing ? 'Nothing' : 'Just (' + show (this.value) + ')'; //. }; //. ``` (function(f) { /* istanbul ignore else */ { module.exports = f (); } } (function() { // $$show :: String var $$show = '@@show'; // seen :: Array Any var seen = []; // entry :: Object -> String -> String function entry(o) { return function(k) { return show (k) + ': ' + show (o[k]); }; } // sortedKeys :: Object -> Array String function sortedKeys(o) { return (Object.keys (o)).sort (); } //# show :: Showable a => a -> String //. //. Returns a useful string representation of the given value. //. //. Dispatches to the value's `@@show` method if present. //. //. Where practical, `show (eval ('(' + show (x) + ')')) = show (x)`. //. //. ```javascript //. > show (null) //. 'null' //. //. > show (undefined) //. 'undefined' //. //. > show (true) //. 'true' //. //. > show (new Boolean (false)) //. 'new Boolean (false)' //. //. > show (-0) //. '-0' //. //. > show (NaN) //. 'NaN' //. //. > show (new Number (Infinity)) //. 'new Number (Infinity)' //. //. > show ('foo\n"bar"\nbaz\n') //. '"foo\\n\\"bar\\"\\nbaz\\n"' //. //. > show (new String ('')) //. 'new String ("")' //. //. > show (['foo', 'bar', 'baz']) //. '["foo", "bar", "baz"]' //. //. > show ([[[[[0]]]]]) //. '[[[[[0]]]]]' //. //. > show ({x: [1, 2], y: [3, 4], z: [5, 6]}) //. '{"x": [1, 2], "y": [3, 4], "z": [5, 6]}' //. ``` function show(x) { if (seen.indexOf (x) >= 0) return ''; switch (Object.prototype.toString.call (x)) { case '[object Boolean]': return typeof x === 'object' ? 'new Boolean (' + show (x.valueOf ()) + ')' : x.toString (); case '[object Number]': return typeof x === 'object' ? 'new Number (' + show (x.valueOf ()) + ')' : 1 / x === -Infinity ? '-0' : x.toString (10); case '[object String]': return typeof x === 'object' ? 'new String (' + show (x.valueOf ()) + ')' : JSON.stringify (x); case '[object Date]': return 'new Date (' + show (isNaN (x.valueOf ()) ? NaN : x.toISOString ()) + ')'; case '[object Error]': return 'new ' + x.name + ' (' + show (x.message) + ')'; case '[object Arguments]': return 'function () { return arguments; } (' + (Array.prototype.map.call (x, show)).join (', ') + ')'; case '[object Array]': seen.push (x); try { return '[' + ((x.map (show)).concat ( sortedKeys (x) .filter (function(k) { return !(/^\d+$/.test (k)); }) .map (entry (x)) )).join (', ') + ']'; } finally { seen.pop (); } case '[object Object]': seen.push (x); try { return ( $$show in x && (x.constructor == null || x.constructor.prototype !== x) ? x[$$show] () : '{' + ((sortedKeys (x)).map (entry (x))).join (', ') + '}' ); } finally { seen.pop (); } case '[object Set]': seen.push (x); try { return 'new Set (' + show (Array.from (x.values ())) + ')'; } finally { seen.pop (); } case '[object Map]': seen.push (x); try { return 'new Map (' + show (Array.from (x.entries ())) + ')'; } finally { seen.pop (); } default: return String (x); } } return show; })); }); /* c8 ignore next */ var setImmediate = typeof setImmediate === 'undefined' ? setImmediateFallback : setImmediate; function noop(){} function moop(){ return this } function call(f, x){ return f(x) } function setImmediateFallback(f, x){ return setTimeout(f, 0, x); } function raise(x){ setImmediate(function rethrowErrorDelayedToEscapePromiseCatch(){ throw x; }); } function showArg$1(x){ return sanctuaryShow(x) + ' :: ' + sanctuaryTypeIdentifiers.parse(sanctuaryTypeIdentifiers(x)).name; } function error(message){ return new Error(message); } function typeError(message){ return new TypeError(message); } function invalidArgument(it, at, expected, actual){ return typeError( it + '() expects its ' + ordinal[at] + ' argument to ' + expected + '.' + '\n Actual: ' + showArg$1(actual) ); } function invalidArgumentOf(expected){ return function(it, at, actual){ return invalidArgument(it, at, expected, actual); }; } function invalidArity(f, args){ return new TypeError( f.name + '() expects to be called with a single argument per invocation\n' + ' Saw: ' + args.length + ' arguments' + Array.prototype.slice.call(args).map(function(arg, i){ return '\n ' + ( ordinal[i] ? ordinal[i].charAt(0).toUpperCase() + ordinal[i].slice(1) : 'Argument ' + String(i + 1) ) + ': ' + showArg$1(arg); }).join('') ); } function invalidNamespace(m, x){ return ( 'The Future was not created by ' + namespace + '. ' + 'Make sure you transform other Futures to ' + namespace + ' Futures. ' + 'Got ' + (x ? ('a Future from ' + x) : 'an unscoped Future') + '.' + '\n See: https://github.com/fluture-js/Fluture#casting-futures' ); } function invalidVersion(m, x){ return ( 'The Future was created by ' + (x < version ? 'an older' : 'a newer') + ' version of ' + namespace + '. ' + 'This means that one of the sources which creates Futures is outdated. ' + 'Update this source, or transform its created Futures to be compatible.' + '\n See: https://github.com/fluture-js/Fluture#casting-futures' ); } function invalidFuture(desc, m, s){ var id = sanctuaryTypeIdentifiers.parse(sanctuaryTypeIdentifiers(m)); var info = id.name === name ? '\n' + ( id.namespace !== namespace ? invalidNamespace(m, id.namespace) : id.version !== version ? invalidVersion(m, id.version) : 'Nothing seems wrong. Contact the Fluture maintainers.') : ''; return typeError( desc + ' to be a valid Future.' + info + '\n' + ' Actual: ' + sanctuaryShow(m) + ' :: ' + id.name + (s || '') ); } function invalidFutureArgument(it, at, m, s){ return invalidFuture(it + '() expects its ' + ordinal[at] + ' argument', m, s); } function ensureError(value, fn){ var message; try{ if(value instanceof Error) return value; message = 'A Non-Error was thrown from a Future: ' + sanctuaryShow(value); }catch (_){ message = 'Something was thrown from a Future, but it could not be converted to String'; } var e = error(message); captureStackTrace(e, fn); return e; } function assignUnenumerable(o, prop, value){ Object.defineProperty(o, prop, {value: value, writable: true, configurable: true}); } function wrapException(caught, callingFuture){ var origin = ensureError(caught, wrapException); var context = cat(origin.context || nil, callingFuture.context); var e = error(origin.message); assignUnenumerable(e, 'future', origin.future || callingFuture); assignUnenumerable(e, 'reason', origin.reason || origin); assignUnenumerable(e, 'stack', e.reason.stack); return withExtraContext(e, context); } function withExtraContext(e, context){ assignUnenumerable(e, 'context', context); assignUnenumerable(e, 'stack', e.stack + contextToStackTrace(context)); return e; } function contextToStackTrace(context){ var stack = '', tail = context; while(tail !== nil){ stack = stack + '\n' + tail.head.stack; tail = tail.tail; } return stack; } function isFunction(f){ return typeof f === 'function'; } function isThenable(m){ return m instanceof Promise || m != null && isFunction(m.then); } function isBoolean(f){ return typeof f === 'boolean'; } function isNumber(f){ return typeof f === 'number'; } function isUnsigned(n){ return (n === Infinity || isNumber(n) && n > 0 && n % 1 === 0); } function isObject(o){ return o !== null && typeof o === 'object'; } function isIterator(i){ return isObject(i) && isFunction(i.next); } function isArray(x){ return Array.isArray(x); } function hasMethod(method, x){ return x != null && isFunction(x[method]); } function isFunctor(x){ return hasMethod(FL.map, x); } function isAlt(x){ return isFunctor(x) && hasMethod(FL.alt, x); } function isApply(x){ return isFunctor(x) && hasMethod(FL.ap, x); } function isBifunctor(x){ return isFunctor(x) && hasMethod(FL.bimap, x); } function isChain(x){ return isApply(x) && hasMethod(FL.chain, x); } function Next(x){ return {done: false, value: x}; } function Done(x){ return {done: true, value: x}; } function isIteration(x){ return isObject(x) && isBoolean(x.done); } /*eslint no-cond-assign:0, no-constant-condition:0 */ function alwaysTrue(){ return true; } function getArgs(it){ var args = new Array(it.arity); for(var i = 1; i <= it.arity; i++){ args[i - 1] = it['$' + String(i)]; } return args; } function showArg(arg){ return ' (' + sanctuaryShow(arg) + ')'; } var any = {pred: alwaysTrue, error: invalidArgumentOf('be anything')}; var func = {pred: isFunction, error: invalidArgumentOf('be a Function')}; var future = {pred: isFuture, error: invalidFutureArgument}; var positiveInteger = {pred: isUnsigned, error: invalidArgumentOf('be a positive Integer')}; function application(n, f, type, args, prev){ if(args.length < 2 && type.pred(args[0])) return captureApplicationContext(prev, n, f); var e = args.length > 1 ? invalidArity(f, args) : type.error(f.name, n - 1, args[0]); captureStackTrace(e, f); throw withExtraContext(e, prev); } function application1(f, type, args){ return application(1, f, type, args, nil); } function Future(computation){ var context = application1(Future, func, arguments); return new Computation(context, computation); } function isFuture(x){ return x instanceof Future || sanctuaryTypeIdentifiers(x) === $$type$1; } // Compliance with sanctuary-type-identifiers versions 1 and 2. // To prevent sanctuary-type-identifiers version 3 from identifying 'Future' // as being of the type denoted by $$type, we ensure that // Future.constructor.prototype is equal to Future. Future['@@type'] = $$type$1; Future.constructor = {prototype: Future}; Future[FL.of] = resolve; Future[FL.chainRec] = chainRec; Future.prototype['@@type'] = $$type$1; Future.prototype['@@show'] = function Future$show(){ return this.toString(); }; Future.prototype.pipe = function Future$pipe(f){ if(!isFunction(f)) throw invalidArgument('Future#pipe', 0, 'be a Function', f); return f(this); }; Future.prototype[FL.ap] = function Future$FL$ap(other){ var context = captureContext(nil, 'a Fantasy Land dispatch to ap', Future$FL$ap); return other._transform(new ApTransformation(context, this)); }; Future.prototype[FL.map] = function Future$FL$map(mapper){ var context = captureContext(nil, 'a Fantasy Land dispatch to map', Future$FL$map); return this._transform(new MapTransformation(context, mapper)); }; Future.prototype[FL.bimap] = function Future$FL$bimap(lmapper, rmapper){ var context = captureContext(nil, 'a Fantasy Land dispatch to bimap', Future$FL$bimap); return this._transform(new BimapTransformation(context, lmapper, rmapper)); }; Future.prototype[FL.chain] = function Future$FL$chain(mapper){ var context = captureContext(nil, 'a Fantasy Land dispatch to chain', Future$FL$chain); return this._transform(new ChainTransformation(context, mapper)); }; Future.prototype[FL.alt] = function Future$FL$alt(other){ var context = captureContext(nil, 'a Fantasy Land dispatch to alt', Future$FL$alt); return this._transform(new AltTransformation(context, other)); }; Future.prototype.extractLeft = function Future$extractLeft(){ return []; }; Future.prototype.extractRight = function Future$extractRight(){ return []; }; Future.prototype._transform = function Future$transform(transformation){ return new Transformer(transformation.context, this, cons(transformation, nil)); }; Future.prototype.isTransformer = false; Future.prototype.context = nil; Future.prototype.arity = 0; Future.prototype.name = 'future'; Future.prototype.toString = function Future$toString(){ return this.name + getArgs(this).map(showArg).join(''); }; Future.prototype.toJSON = function Future$toJSON(){ return {$: $$type$1, kind: 'interpreter', type: this.name, args: getArgs(this)}; }; function createInterpreter(arity, name, interpret){ var Interpreter = function(context, $1, $2, $3){ this.context = context; this.$1 = $1; this.$2 = $2; this.$3 = $3; }; Interpreter.prototype = Object.create(Future.prototype); Interpreter.prototype.arity = arity; Interpreter.prototype.name = name; Interpreter.prototype._interpret = interpret; return Interpreter; } var Computation = createInterpreter(1, 'Future', function Computation$interpret(rec, rej, res){ var computation = this.$1, open = false, cancel = noop, cont = function(){ open = true; }; try{ cancel = computation(function Computation$rej(x){ cont = function Computation$rej$cont(){ open = false; rej(x); }; if(open){ cont(); } }, function Computation$res(x){ cont = function Computation$res$cont(){ open = false; res(x); }; if(open){ cont(); } }); }catch(e){ rec(wrapException(e, this)); return noop; } if(!(isFunction(cancel) && cancel.length === 0)){ rec(wrapException(typeError( 'The computation was expected to return a nullary cancellation function\n' + ' Actual: ' + sanctuaryShow(cancel) ), this)); return noop; } cont(); return function Computation$cancel(){ if(open){ open = false; cancel && cancel(); } }; }); var Never = createInterpreter(0, 'never', function Never$interpret(){ return noop; }); Never.prototype._isNever = true; var never = new Never(nil); function isNever(x){ return isFuture(x) && x._isNever === true; } var Crash = createInterpreter(1, 'crash', function Crash$interpret(rec){ rec(this.$1); return noop; }); function crash(x){ return new Crash(application1(crash, any, arguments), x); } var Reject = createInterpreter(1, 'reject', function Reject$interpret(rec, rej){ rej(this.$1); return noop; }); Reject.prototype.extractLeft = function Reject$extractLeft(){ return [this.$1]; }; function reject(x){ return new Reject(application1(reject, any, arguments), x); } var Resolve = createInterpreter(1, 'resolve', function Resolve$interpret(rec, rej, res){ res(this.$1); return noop; }); Resolve.prototype.extractRight = function Resolve$extractRight(){ return [this.$1]; }; function resolve(x){ return new Resolve(application1(resolve, any, arguments), x); } //Note: This function is not curried because it's only used to satisfy the // Fantasy Land ChainRec specification. function chainRec(step, init){ return resolve(Next(init))._transform(new ChainTransformation(nil, function chainRec$recur(o){ return o.done ? resolve(o.value) : step(Next, Done, o.value)._transform(new ChainTransformation(nil, chainRec$recur)); })); } var Transformer = createInterpreter(2, 'transform', function Transformer$interpret(rec, rej, res){ //These are the cold, and hot, transformation stacks. The cold actions are those that //have yet to run parallel computations, and hot are those that have. var cold = nil, hot = nil; //These combined variables define our current state. // future = the future we are currently forking // transformation = the transformation to be informed when the future settles // cancel = the cancel function of the current future // settled = a boolean indicating whether a new tick should start // async = a boolean indicating whether we are awaiting a result asynchronously var future, transformation, cancel = noop, settled, async = true, it; //Takes a transformation from the top of the hot stack and returns it. function nextHot(){ var x = hot.head; hot = hot.tail; return x; } //Takes a transformation from the top of the cold stack and returns it. function nextCold(){ var x = cold.head; cold = cold.tail; return x; } //This function is called with a future to use in the next tick. //Here we "flatten" the actions of another Sequence into our own actions, //this is the magic that allows for infinitely stack safe recursion because //actions like ChainAction will return a new Sequence. //If we settled asynchronously, we call drain() directly to run the next tick. function settle(m){ settled = true; future = m; if(future.isTransformer){ var tail = future.$2; while(!isNil(tail)){ cold = cons(tail.head, cold); tail = tail.tail; } future = future.$1; } if(async) drain(); } //This function serves as a rejection handler for our current future. //It will tell the current transformation that the future rejected, and it will //settle the current tick with the transformation's answer to that. function rejected(x){ settle(transformation.rejected(x)); } //This function serves as a resolution handler for our current future. //It will tell the current transformation that the future resolved, and it will //settle the current tick with the transformation's answer to that. function resolved(x){ settle(transformation.resolved(x)); } //This function is passed into actions when they are "warmed up". //If the transformation decides that it has its result, without the need to await //anything else, then it can call this function to force "early termination". //When early termination occurs, all actions which were stacked prior to the //terminator will be skipped. If they were already hot, they will also be //sent a cancel signal so they can cancel their own concurrent computations, //as their results are no longer needed. function early(m, terminator){ cancel(); cold = nil; if(async && transformation !== terminator){ transformation.cancel(); while((it = nextHot()) && it !== terminator) it.cancel(); } settle(m); } //This will cancel the current Future, the current transformation, and all stacked hot actions. function Sequence$cancel(){ cancel(); transformation && transformation.cancel(); while(it = nextHot()) it.cancel(); } //This function is called when an exception is caught. function exception(e){ Sequence$cancel(); settled = true; cold = hot = nil; var error = wrapException(e, future); future = never; rec(error); } //This function serves to kickstart concurrent computations. //Takes all actions from the cold stack in reverse order, and calls run() on //each of them, passing them the "early" function. If any of them settles (by //calling early()), we abort. After warming up all actions in the cold queue, //we warm up the current transformation as well. function warmupActions(){ cold = reverse(cold); while(cold !== nil){ it = cold.head.run(early); if(settled) return; hot = cons(it, hot); cold = cold.tail; } transformation = transformation.run(early); } //This function represents our main execution loop. By "tick", we've been //referring to the execution of one iteration in the while-loop below. function drain(){ async = false; while(true){ settled = false; if(transformation = nextCold()){ cancel = future._interpret(exception, rejected, resolved); if(!settled) warmupActions(); }else if(transformation = nextHot()){ cancel = future._interpret(exception, rejected, resolved); }else break; if(settled) continue; async = true; return; } cancel = future._interpret(exception, rej, res); } //Start the execution loop. settle(this); //Return the cancellation function. return Sequence$cancel; }); Transformer.prototype.isTransformer = true; Transformer.prototype._transform = function Transformer$_transform(transformation){ return new Transformer(transformation.context, this.$1, cons(transformation, this.$2)); }; Transformer.prototype.toString = function Transformer$toString(){ return toArray(reverse(this.$2)).reduce(function(str, action){ return action.name + getArgs(action).map(showArg).join('') + ' (' + str + ')'; }, this.$1.toString()); }; function BaseTransformation$rejected(x){ this.cancel(); return new Reject(this.context, x); } function BaseTransformation$resolved(x){ this.cancel(); return new Resolve(this.context, x); } function BaseTransformation$toJSON(){ return {$: $$type$1, kind: 'transformation', type: this.name, args: getArgs(this)}; } var BaseTransformation = { rejected: BaseTransformation$rejected, resolved: BaseTransformation$resolved, run: moop, cancel: noop, context: nil, arity: 0, name: 'transform', toJSON: BaseTransformation$toJSON }; function wrapHandler(handler){ return function transformationHandler(x){ var m; try{ m = handler.call(this, x); }catch(e){ return new Crash(this.context, e); } if(isFuture(m)){ return m; } return new Crash(this.context, invalidFuture( this.name + ' expects the return value from the function it\'s given', m, '\n When called with: ' + sanctuaryShow(x) )); }; } function createTransformation(arity, name, prototype){ var Transformation = function(context, $1, $2){ this.context = context; this.$1 = $1; this.$2 = $2; }; Transformation.prototype = Object.create(BaseTransformation); Transformation.prototype.arity = arity; Transformation.prototype.name = name; if(typeof prototype.rejected === 'function'){ Transformation.prototype.rejected = wrapHandler(prototype.rejected); } if(typeof prototype.resolved === 'function'){ Transformation.prototype.resolved = wrapHandler(prototype.resolved); } if(typeof prototype.run === 'function'){ Transformation.prototype.run = prototype.run; } return Transformation; } var ApTransformation = createTransformation(1, 'ap', { resolved: function ApTransformation$resolved(f){ if(isFunction(f)) return this.$1._transform(new MapTransformation(this.context, f)); throw typeError( 'ap expects the second Future to resolve to a Function\n' + ' Actual: ' + sanctuaryShow(f) ); } }); var AltTransformation = createTransformation(1, 'alt', { rejected: function AltTransformation$rejected(){ return this.$1 } }); var MapTransformation = createTransformation(1, 'map', { resolved: function MapTransformation$resolved(x){ return new Resolve(this.context, call(this.$1, x)); } }); var BimapTransformation = createTransformation(2, 'bimap', { rejected: function BimapTransformation$rejected(x){ return new Reject(this.context, call(this.$1, x)); }, resolved: function BimapTransformation$resolved(x){ return new Resolve(this.context, call(this.$2, x)); } }); var ChainTransformation = createTransformation(1, 'chain', { resolved: function ChainTransformation$resolved(x){ return call(this.$1, x) } }); var After = createInterpreter(2, 'after', function After$interpret(rec, rej, res){ var id = setTimeout(res, this.$1, this.$2); return function After$cancel(){ clearTimeout(id); }; }); After.prototype.extractRight = function After$extractRight(){ return [this.$2]; }; function alwaysNever$1(_){ return never; } function after(time){ var context1 = application1(after, positiveInteger, arguments); return time === Infinity ? alwaysNever$1 : (function after(value){ var context2 = application(2, after, any, arguments, context1); return new After(context2, time, value); }); } var alternative = {pred: isAlt, error: invalidArgumentOf('have Alt implemented')}; function alt(left){ if(isFuture(left)){ var context1 = application1(alt, future, arguments); return function alt(right){ var context2 = application(2, alt, future, arguments, context1); return right._transform(new AltTransformation(context2, left)); }; } var context = application1(alt, alternative, arguments); return function alt(right){ application(2, alt, alternative, arguments, context); return left[FL.alt](right); }; } var AndTransformation = createTransformation(1, 'and', { resolved: function AndTransformation$resolved(){ return this.$1 } }); function and(left){ var context1 = application1(and, future, arguments); return function and(right){ var context2 = application(2, and, future, arguments, context1); return right._transform(new AndTransformation(context2, left)); }; } var apply = {pred: isApply, error: invalidArgumentOf('have Apply implemented')}; function ap(mx){ if(isFuture(mx)){ var context1 = application1(ap, future, arguments); return function ap(mf){ var context2 = application(2, ap, future, arguments, context1); return mf._transform(new ApTransformation(context2, mx)); }; } var context = application1(ap, apply, arguments); return function ap(mf){ application(2, ap, apply, arguments, context); return mx[FL.ap](mf); }; } function invalidPromise(p, f, a){ return typeError( 'encaseP() expects the function it\'s given to return a Promise/Thenable' + '\n Actual: ' + sanctuaryShow(p) + '\n From calling: ' + sanctuaryShow(f) + '\n With: ' + sanctuaryShow(a) ); } var EncaseP = createInterpreter(2, 'encaseP', function EncaseP$interpret(rec, rej, res){ var open = true, fn = this.$1, arg = this.$2, p; try{ p = fn(arg); }catch(e){ rec(wrapException(e, this)); return noop; } if(!isThenable(p)){ rec(wrapException(invalidPromise(p, fn, arg), this)); return noop; } p.then(function EncaseP$res(x){ if(open){ open = false; res(x); } }, function EncaseP$rej(x){ if(open){ open = false; rej(x); } }); return function EncaseP$cancel(){ open = false; }; }); function encaseP(f){ var context1 = application1(encaseP, func, arguments); return function encaseP(x){ var context2 = application(2, encaseP, any, arguments, context1); return new EncaseP(context2, f, x); }; } function attemptP(_){ return encaseP.apply(this, arguments)(undefined); } var Encase = createInterpreter(2, 'encase', function Encase$interpret(rec, rej, res){ var fn = this.$1, r; try{ r = fn(this.$2); }catch(e){ rej(e); return noop } res(r); return noop; }); function encase(f){ var context1 = application1(encase, func, arguments); return function encase(x){ var context2 = application(2, encase, any, arguments, context1); return new Encase(context2, f, x); }; } function attempt(_){ return encase.apply(this, arguments)(undefined); } var bifunctor = {pred: isBifunctor, error: invalidArgumentOf('have Bifunctor implemented')}; function bimap(f){ var context1 = application1(bimap, func, arguments); return function bimap(g){ var context2 = application(2, bimap, func, arguments, context1); return function bimap(m){ var context3 = application(3, bimap, bifunctor, arguments, context2); return isFuture(m) ? m._transform(new BimapTransformation(context3, f, g)) : m[FL.bimap](f, g); }; }; } var BichainTransformation = createTransformation(2, 'bichain', { rejected: function BichainTransformation$rejected(x){ return call(this.$1, x) }, resolved: function BichainTransformation$resolved(x){ return call(this.$2, x) } }); function bichain(f){ var context1 = application1(bichain, func, arguments); return function bichain(g){ var context2 = application(2, bichain, func, arguments, context1); return function bichain(m){ var context3 = application(3, bichain, future, arguments, context2); return m._transform(new BichainTransformation(context3, f, g)); }; }; } function Eager(future){ var _this = this; _this.rec = noop; _this.rej = noop; _this.res = noop; _this.crashed = false; _this.rejected = false; _this.resolved = false; _this.value = null; _this.cancel = future._interpret(function Eager$crash(x){ _this.value = x; _this.crashed = true; _this.cancel = noop; _this.rec(x); }, function Eager$reject(x){ _this.value = x; _this.rejected = true; _this.cancel = noop; _this.rej(x); }, function Eager$resolve(x){ _this.value = x; _this.resolved = true; _this.cancel = noop; _this.res(x); }); } Eager.prototype = Object.create(Future.prototype); Eager.prototype._interpret = function Eager$interpret(rec, rej, res){ if(this.crashed) rec(this.value); else if(this.rejected) rej(this.value); else if(this.resolved) res(this.value); else { this.rec = rec; this.rej = rej; this.res = res; } return this.cancel; }; function earlyCrash(early, x){ early(crash(x)); } function earlyReject(early, x){ early(reject(x)); } function earlyResolve(early, x){ early(resolve(x)); } function createParallelTransformation(name, rec, rej, res, prototype){ var ParallelTransformation = createTransformation(1, name, Object.assign({ run: function Parallel$run(early){ var eager = new Eager(this.$1); var transformation = new ParallelTransformation(this.context, eager); function Parallel$early(m){ early(m, transformation); } transformation.cancel = eager._interpret( function Parallel$rec(x){ rec(Parallel$early, x); }, function Parallel$rej(x){ rej(Parallel$early, x); }, function Parallel$res(x){ res(Parallel$early, x); } ); return transformation; } }, prototype)); return ParallelTransformation; } var PairTransformation = createTransformation(1, 'pair', { resolved: function PairTransformation$resolved(x){ return new Resolve(this.context, [x, this.$1]); } }); var BothTransformation = createParallelTransformation('both', earlyCrash, earlyReject, noop, { resolved: function BothTransformation$resolved(x){ return this.$1._transform(new PairTransformation(this.context, x)); } }); function both(left){ var context1 = application1(both, future, arguments); return function both(right){ var context2 = application(2, both, future, arguments, context1); return right._transform(new BothTransformation(context2, left)); }; } var Cold = 0; var Pending = 1; var Crashed = 2; var Rejected = 3; var Resolved = 4; function Queued(rec, rej, res){ this[Crashed] = rec; this[Rejected] = rej; this[Resolved] = res; } var Cache = createInterpreter(1, 'cache', function Cache$interpret(rec, rej, res){ var cancel = noop; switch(this._state){ /* c8 ignore next 4 */ case Pending: cancel = this._addToQueue(rec, rej, res); break; case Crashed: rec(this._value); break; case Rejected: rej(this._value); break; case Resolved: res(this._value); break; default: this._queue = []; cancel = this._addToQueue(rec, rej, res); this.run(); } return cancel; }); Cache.prototype._cancel = noop; Cache.prototype._queue = null; Cache.prototype._queued = 0; Cache.prototype._value = undefined; Cache.prototype._state = Cold; Cache.prototype.extractLeft = function Cache$extractLeft(){ return this._state === Rejected ? [this._value] : []; }; Cache.prototype.extractRight = function Cache$extractRight(){ return this._state === Resolved ? [this._value] : []; }; Cache.prototype._addToQueue = function Cache$addToQueue(rec, rej, res){ var _this = this; if(_this._state > Pending) return noop; var i = _this._queue.push(new Queued(rec, rej, res)) - 1; _this._queued = _this._queued + 1; return function Cache$removeFromQueue(){ if(_this._state > Pending) return; _this._queue[i] = undefined; _this._queued = _this._queued - 1; if(_this._queued === 0) _this.reset(); }; }; Cache.prototype._drainQueue = function Cache$drainQueue(){ if(this._state <= Pending) return; if(this._queued === 0) return; var queue = this._queue; var length = queue.length; var state = this._state; var value = this._value; for(var i = 0; i < length; i++){ queue[i] && queue[i][state](value); queue[i] = undefined; } this._queue = undefined; this._queued = 0; }; Cache.prototype.crash = function Cache$crash(error){ if(this._state > Pending) return; this._value = error; this._state = Crashed; this._drainQueue(); }; Cache.prototype.reject = function Cache$reject(reason){ if(this._state > Pending) return; this._value = reason; this._state = Rejected; this._drainQueue(); }; Cache.prototype.resolve = function Cache$resolve(value){ if(this._state > Pending) return; this._value = value; this._state = Resolved; this._drainQueue(); }; Cache.prototype.run = function Cache$run(){ var _this = this; if(_this._state > Cold) return; _this._state = Pending; _this._cancel = _this.$1._interpret( function Cache$fork$rec(x){ _this.crash(x); }, function Cache$fork$rej(x){ _this.reject(x); }, function Cache$fork$res(x){ _this.resolve(x); } ); }; Cache.prototype.reset = function Cache$reset(){ if(this._state === Cold) return; if(this._state === Pending) this._cancel(); this._cancel = noop; this._queue = []; this._queued = 0; this._value = undefined; this._state = Cold; }; function cache(m){ return new Cache(application1(cache, future, arguments), m); } var ChainRejTransformation = createTransformation(1, 'chainRej', { rejected: function ChainRejTransformation$rejected(x){ return call(this.$1, x) } }); function chainRej(f){ var context1 = application1(chainRej, func, arguments); return function chainRej(m){ var context2 = application(2, chainRej, future, arguments, context1); return m._transform(new ChainRejTransformation(context2, f)); }; } var monad = {pred: isChain, error: invalidArgumentOf('have Chain implemented')}; function chain(f){ var context1 = application1(chain, func, arguments); return function chain(m){ var context2 = application(2, chain, monad, arguments, context1); return isFuture(m) ? m._transform(new ChainTransformation(context2, f)) : m[FL.chain](f); }; } function done(callback){ var context1 = application1(done, func, arguments); function done$res(x){ callback(null, x); } return function done(m){ application(2, done, future, arguments, context1); return m._interpret(raise, callback, done$res); }; } function extractLeft(m){ application1(extractLeft, future, arguments); return m.extractLeft(); } function extractRight(m){ application1(extractRight, future, arguments); return m.extractRight(); } var CoalesceTransformation = createTransformation(2, 'coalesce', { rejected: function CoalesceTransformation$rejected(x){ return new Resolve(this.context, call(this.$1, x)); }, resolved: function CoalesceTransformation$resolved(x){ return new Resolve(this.context, call(this.$2, x)); } }); function coalesce(f){ var context1 = application1(coalesce, func, arguments); return function coalesce(g){ var context2 = application(2, coalesce, func, arguments, context1); return function coalesce(m){ var context3 = application(3, coalesce, future, arguments, context2); return m._transform(new CoalesceTransformation(context3, f, g)); }; }; } function forkCatch(f){ var context1 = application1(forkCatch, func, arguments); return function forkCatch(g){ var context2 = application(2, forkCatch, func, arguments, context1); return function forkCatch(h){ var context3 = application(3, forkCatch, func, arguments, context2); return function forkCatch(m){ application(4, forkCatch, future, arguments, context3); return m._interpret(f, g, h); }; }; }; } function fork(f){ var context1 = application1(fork, func, arguments); return function fork(g){ var context2 = application(2, fork, func, arguments, context1); return function fork(m){ application(3, fork, future, arguments, context2); return m._interpret(raise, f, g); }; }; } var Undetermined = 0; var Synchronous = 1; var Asynchronous = 2; /*eslint consistent-return: 0 */ function invalidIteration(o){ return typeError( 'The iterator did not return a valid iteration from iterator.next()\n' + ' Actual: ' + sanctuaryShow(o) ); } function invalidState(x){ return invalidFuture( 'go() expects the value produced by the iterator', x, '\n Tip: If you\'re using a generator, make sure you always yield a Future' ); } var Go = createInterpreter(1, 'go', function Go$interpret(rec, rej, res){ var _this = this, timing = Undetermined, cancel = noop, state, value, iterator; function crash(e){ rec(wrapException(e, _this)); } try{ iterator = _this.$1(); }catch(e){ crash(e); return noop; } if(!isIterator(iterator)){ crash(invalidArgument('go', 0, 'return an iterator, maybe you forgot the "*"', iterator)); return noop; } function resolved(x){ value = x; if(timing === Asynchronous) return drain(); timing = Synchronous; } function drain(){ //eslint-disable-next-line no-constant-condition while(true){ try{ state = iterator.next(value); }catch(e){ return crash(e); } if(!isIteration(state)) return crash(invalidIteration(state)); if(state.done) break; if(!isFuture(state.value)){ return crash(invalidState(state.value)); } timing = Undetermined; cancel = state.value._interpret(crash, rej, resolved); if(timing === Undetermined) return timing = Asynchronous; } res(state.value); } drain(); return function Go$cancel(){ cancel(); }; }); function go(generator){ return new Go(application1(go, func, arguments), generator); } function invalidDisposal(m, f, x){ return invalidFuture( 'hook() expects the return value from the first function it\'s given', m, '\n From calling: ' + sanctuaryShow(f) + '\n With: ' + sanctuaryShow(x) ); } function invalidConsumption(m, f, x){ return invalidFuture( 'hook() expects the return value from the second function it\'s given', m, '\n From calling: ' + sanctuaryShow(f) + '\n With: ' + sanctuaryShow(x) ); } var Hook = createInterpreter(3, 'hook', function Hook$interpret(rec, rej, res){ var _this = this, _acquire = this.$1, _dispose = this.$2, _consume = this.$3; var cancel, cancelConsume = noop, resource, value, cont = noop; function Hook$done(){ cont(value); } function Hook$rec(x){ rec(wrapException(x, _this)); } function Hook$dispose(){ var disposal; try{ disposal = _dispose(resource); }catch(e){ return Hook$rec(e); } if(!isFuture(disposal)){ return Hook$rec(invalidDisposal(disposal, _dispose, resource)); } cancel = Hook$cancelDisposal; disposal._interpret(Hook$rec, Hook$disposalRejected, Hook$done); } function Hook$cancelConsumption(){ cancelConsume(); Hook$dispose(); Hook$cancelDisposal(); } function Hook$cancelDisposal(){ cont = noop; } function Hook$disposalRejected(x){ Hook$rec(new Error('The disposal Future rejected with ' + sanctuaryShow(x))); } function Hook$consumptionException(x){ cont = Hook$rec; value = x; Hook$dispose(); } function Hook$consumptionRejected(x){ cont = rej; value = x; Hook$dispose(); } function Hook$consumptionResolved(x){ cont = res; value = x; Hook$dispose(); } function Hook$consume(x){ resource = x; var consumption; try{ consumption = _consume(resource); }catch(e){ return Hook$consumptionException(e); } if(!isFuture(consumption)){ return Hook$consumptionException(invalidConsumption(consumption, _consume, resource)); } cancel = Hook$cancelConsumption; cancelConsume = consumption._interpret( Hook$consumptionException, Hook$consumptionRejected, Hook$consumptionResolved ); } var cancelAcquire = _acquire._interpret(Hook$rec, rej, Hook$consume); cancel = cancel || cancelAcquire; return function Hook$fork$cancel(){ rec = raise; cancel(); }; }); function hook(acquire){ var context1 = application1(hook, future, arguments); return function hook(dispose){ var context2 = application(2, hook, func, arguments, context1); return function hook(consume){ var context3 = application(3, hook, func, arguments, context2); return new Hook(context3, acquire, dispose, consume); }; }; } var LastlyTransformation = createTransformation(1, 'lastly', { rejected: function LastlyAction$rejected(x){ return this.$1._transform(new AndTransformation(this.context, new Reject(this.context, x))); }, resolved: function LastlyAction$resolved(x){ return this.$1._transform(new AndTransformation(this.context, new Resolve(this.context, x))); } }); function lastly(cleanup){ var context1 = application1(lastly, future, arguments); return function lastly(program){ var context2 = application(2, lastly, future, arguments, context1); return program._transform(new LastlyTransformation(context2, cleanup)); }; } var MapRejTransformation = createTransformation(1, 'mapRej', { rejected: function MapRejTransformation$rejected(x){ return new Reject(this.context, call(this.$1, x)); } }); function mapRej(f){ var context1 = application1(mapRej, func, arguments); return function mapRej(m){ var context2 = application(2, mapRej, future, arguments, context1); return m._transform(new MapRejTransformation(context2, f)); }; } var functor = {pred: isFunctor, error: invalidArgumentOf('have Functor implemented')}; function map(f){ var context1 = application1(map, func, arguments); return function map(m){ var context2 = application(2, map, functor, arguments, context1); return isFuture(m) ? m._transform(new MapTransformation(context2, f)) : m[FL.map](f); }; } var Node = createInterpreter(1, 'node', function Node$interpret(rec, rej, res){ function Node$done(err, val){ cont = err ? function EncaseN3$rej(){ open = false; rej(err); } : function EncaseN3$res(){ open = false; res(val); }; if(open){ cont(); } } var open = false, cont = function(){ open = true; }; try{ call(this.$1, Node$done); }catch(e){ rec(wrapException(e, this)); open = false; return noop; } cont(); return function Node$cancel(){ open = false; }; }); function node(f){ return new Node(application1(node, func, arguments), f); } var ParallelApTransformation = createParallelTransformation('pap', earlyCrash, earlyReject, noop, { resolved: function ParallelApTransformation$resolved(f){ if(isFunction(f)) return this.$1._transform(new MapTransformation(this.context, f)); throw typeError( 'pap expects the second Future to resolve to a Function\n' + ' Actual: ' + sanctuaryShow(f) ); } }); function pap(mx){ var context1 = application1(pap, future, arguments); return function pap(mf){ var context2 = application(2, pap, future, arguments, context1); return mf._transform(new ParallelApTransformation(context2, mx)); }; } function isFutureArray(xs){ if(!isArray(xs)) return false; for(var i = 0; i < xs.length; i++){ if(!isFuture(xs[i])) return false; } return true; } var futureArray = { pred: isFutureArray, error: invalidArgumentOf('be an Array of valid Futures') }; var Parallel = createInterpreter(2, 'parallel', function Parallel$interpret(rec, rej, res){ var _this = this, futures = this.$2, length = futures.length; var max = Math.min(this.$1, length), cancels = new Array(length), out = new Array(length); var cursor = 0, running = 0, blocked = false, cont = noop; function Parallel$cancel(){ rec = noop; rej = noop; res = noop; cursor = length; for(var n = 0; n < length; n++) cancels[n] && cancels[n](); } function Parallel$run(idx){ running++; cancels[idx] = futures[idx]._interpret(function Parallel$rec(e){ cont = rec; cancels[idx] = noop; Parallel$cancel(); cont(wrapException(e, _this)); }, function Parallel$rej(reason){ cont = rej; cancels[idx] = noop; Parallel$cancel(); cont(reason); }, function Parallel$res(value){ cancels[idx] = noop; out[idx] = value; running--; if(cursor === length && running === 0) res(out); else if(blocked) Parallel$drain(); }); } function Parallel$drain(){ blocked = false; while(cursor < length && running < max) Parallel$run(cursor++); blocked = true; } Parallel$drain(); return Parallel$cancel; }); var emptyArray = resolve([]); function parallel$1(max){ var context1 = application1(parallel$1, positiveInteger, arguments); return function parallel(ms){ var context2 = application(2, parallel, futureArray, arguments, context1); return ms.length === 0 ? emptyArray : new Parallel(context2, max, ms); }; } var RaceTransformation = createParallelTransformation('race', earlyCrash, earlyReject, earlyResolve, {}); function race(left){ var context1 = application1(race, future, arguments); return function race(right){ var context2 = application(2, race, future, arguments, context1); return right._transform(new RaceTransformation(context2, left)); }; } function ConcurrentFuture (sequential){ this.sequential = sequential; } ConcurrentFuture.prototype = Object.create(Par.prototype); function Par (sequential){ if(!isFuture(sequential)) throw invalidFutureArgument(Par.name, 0, sequential); return new ConcurrentFuture(sequential); } var $$type = namespace + '/ConcurrentFuture@' + version; var zeroInstance = new ConcurrentFuture(never); // Compliance with sanctuary-type-identifiers versions 1 and 2. // To prevent sanctuary-type-identifiers version 3 from identifying // 'Par' as being of the type denoted by $$type, we ensure that // Par.constructor.prototype is equal to Par. Par['@@type'] = $$type; Par.constructor = {prototype: Par}; Par[FL.of] = function Par$of(x){ return new ConcurrentFuture(resolve(x)); }; Par[FL.zero] = function Par$zero(){ return zeroInstance; }; Par.prototype['@@type'] = $$type; Par.prototype['@@show'] = function Par$show(){ return this.toString(); }; Par.prototype.toString = function Par$toString(){ return 'Par (' + this.sequential.toString() + ')'; }; Par.prototype[FL.map] = function Par$FL$map(f){ var context = captureContext( nil, 'a Fantasy Land dispatch to map via ConcurrentFuture', Par$FL$map ); return new ConcurrentFuture(this.sequential._transform(new MapTransformation(context, f))); }; Par.prototype[FL.ap] = function Par$FL$ap(other){ var context = captureContext( nil, 'a Fantasy Land dispatch to ap via ConcurrentFuture', Par$FL$ap ); return new ConcurrentFuture(other.sequential._transform( new ParallelApTransformation(context, this.sequential) )); }; Par.prototype[FL.alt] = function Par$FL$alt(other){ var context = captureContext( nil, 'a Fantasy Land dispatch to alt via ConcurrentFuture', Par$FL$alt ); return new ConcurrentFuture(other.sequential._transform( new RaceTransformation(context, this.sequential) )); }; function isParallel(x){ return x instanceof ConcurrentFuture || sanctuaryTypeIdentifiers(x) === $$type; } function promise(m){ application1(promise, future, arguments); return new Promise(function promise$computation(res, rej){ m._interpret(rej, rej, res); }); } var RejectAfter = createInterpreter(2, 'rejectAfter', function RejectAfter$interpret(rec, rej){ var id = setTimeout(rej, this.$1, this.$2); return function RejectAfter$cancel(){ clearTimeout(id); }; }); RejectAfter.prototype.extractLeft = function RejectAfter$extractLeft(){ return [this.$2]; }; function alwaysNever(_){ return never; } function rejectAfter(time){ var context1 = application1(rejectAfter, positiveInteger, arguments); return time === Infinity ? alwaysNever : (function rejectAfter(value){ var context2 = application(2, rejectAfter, any, arguments, context1); return new RejectAfter(context2, time, value); }); } var parallel = {pred: isParallel, error: invalidArgumentOf('be a ConcurrentFuture')}; function seq(par){ application1(seq, parallel, arguments); return par.sequential; } var SwapTransformation = createTransformation(0, 'swap', { resolved: function SwapTransformation$resolved(x){ return new Reject(this.context, x); }, rejected: function SwapTransformation$rejected(x){ return new Resolve(this.context, x); } }); function swap(m){ var context = application1(swap, future, arguments); return m._transform(new SwapTransformation(context)); } function value(res){ var context1 = application1(value, func, arguments); return function value(m){ application(2, value, future, arguments, context1); function value$rej(x){ raise(error( 'Future#value was called on a rejected Future\n' + ' Rejection: ' + sanctuaryShow(x) + '\n' + ' Future: ' + sanctuaryShow(m) )); } return m._interpret(raise, value$rej, res); }; } var Fluture = /*#__PURE__*/Object.freeze({ __proto__: null, 'default': Future, Future: Future, isFuture: isFuture, isNever: isNever, never: never, reject: reject, resolve: resolve, after: after, alt: alt, and: and, ap: ap, attemptP: attemptP, attempt: attempt, bimap: bimap, bichain: bichain, both: both, cache: cache, chainRej: chainRej, chain: chain, done: done, encaseP: encaseP, encase: encase, extractLeft: extractLeft, extractRight: extractRight, coalesce: coalesce, forkCatch: forkCatch, fork: fork, go: go, hook: hook, lastly: lastly, mapRej: mapRej, map: map, node: node, pap: pap, parallel: parallel$1, Par: Par, promise: promise, race: race, rejectAfter: rejectAfter, seq: seq, swap: swap, value: value, debugMode: debugMode }); var index_cjs = Object.assign(Future, Fluture); return index_cjs; }()); /** Fluture license The MIT License (MIT) Copyright (c) 2020 Aldwin Vlasblom Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** sanctuary-show license The MIT License (MIT) Copyright (c) 2020 Sanctuary Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** sanctuary-type-identifiers license The MIT License (MIT) Copyright (c) 2019 Sanctuary Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ ================================================ FILE: dist/module.js ================================================ /** * Fluture bundled; version 14.0.0 */ /// function createCommonjsModule(fn) { var module = { exports: {} }; return fn(module, module.exports), module.exports; } /* @@@@@@@ @@@@@@@ @@ @@ @@ @@ @@ @@@ @@ @@@ @@ @@ @@ @@@ @@ @@ @@@@@@ @@ @@@ @@ @@@ @@@@ @@ @@ @@@ @@ @@ @@ @@@ @@ @@@ @@ @@@ @@@ @@ @@@ @@ @@ @@ @@@ @@ @@ @@ @@@ @@ @@@ @@ @@@ @@@ @@ @@@@@@@@ @@ @@ @@@ @@ @@ @@ @@@ @@ @@@ @@ @@@ @@@ @@ @@@ @@ @@@ @@@@@ @@ @@@ @@@@@ @@@ @@@ @@ @@@@@@ @@@@@ @@ @@ @@ @@ @@@@@@@ @@@@@@@ @@@@@ @@ */ var sanctuaryTypeIdentifiers = createCommonjsModule(function (module) { //. # sanctuary-type-identifiers //. //. A type is a set of values. Boolean, for example, is the type comprising //. `true` and `false`. A value may be a member of multiple types (`42` is a //. member of Number, PositiveNumber, Integer, and many other types). //. //. In certain situations it is useful to divide JavaScript values into //. non-overlapping types. The language provides two constructs for this //. purpose: the [`typeof`][1] operator and [`Object.prototype.toString`][2]. //. Each has pros and cons, but neither supports user-defined types. //. //. sanctuary-type-identifiers comprises: //. //. - an npm and browser -compatible package for deriving the //. _type identifier_ of a JavaScript value; and //. - a specification which authors may follow to specify type //. identifiers for their types. //. //. ### Specification //. //. For a type to be compatible with the algorithm: //. //. - every member of the type MUST have a `@@type` property //. (the _type identifier_); and //. //. - the type identifier MUST be a string primitive and SHOULD have //. format `'/[@]'`, where: //. //. - `` MUST consist of one or more characters, and //. SHOULD equal the name of the npm package which defines the //. type (including [scope][3] where appropriate); //. //. - `` MUST consist of one or more characters, and SHOULD //. be the unique name of the type; and //. //. - `` MUST consist of one or more digits, and SHOULD //. represent the version of the type. //. //. If the type identifier does not conform to the format specified above, //. it is assumed that the entire string represents the _name_ of the type; //. _namespace_ will be `null` and _version_ will be `0`. //. //. If the _version_ is not given, it is assumed to be `0`. (function(f) { /* istanbul ignore else */ { module.exports = f (); } } (function() { // $$type :: String var $$type = '@@type'; // pattern :: RegExp var pattern = new RegExp ( '^' + '([\\s\\S]+)' // + '/' // SOLIDUS (U+002F) + '([\\s\\S]+?)' // + '(?:' // optional non-capturing group { + '@' // COMMERCIAL AT (U+0040) + '([0-9]+)' // + ')?' // } + '$' ); //. ### Usage //. //. ```javascript //. const type = require ('sanctuary-type-identifiers'); //. ``` //. //. ```javascript //. > const Identity$prototype = { //. . '@@type': 'my-package/Identity@1', //. . '@@show': function() { //. . return 'Identity (' + show (this.value) + ')'; //. . } //. . } //. //. > const Identity = value => //. . Object.assign (Object.create (Identity$prototype), {value}) //. //. > type (Identity (0)) //. 'my-package/Identity@1' //. //. > type.parse (type (Identity (0))) //. {namespace: 'my-package', name: 'Identity', version: 1} //. ``` //. //. ### API //. //# type :: Any -> String //. //. Takes any value and returns a string which identifies its type. If the //. value conforms to the [specification][4], the custom type identifier is //. returned. //. //. ```javascript //. > type (null) //. 'Null' //. //. > type (true) //. 'Boolean' //. //. > type (Identity (0)) //. 'my-package/Identity@1' //. ``` function type(x) { return x != null && x.constructor != null && x.constructor.prototype !== x && typeof x[$$type] === 'string' ? x[$$type] : (Object.prototype.toString.call (x)).slice ('[object '.length, -']'.length); } //# type.parse :: String -> { namespace :: Nullable String, name :: String, version :: Number } //. //. Takes any string and parses it according to the [specification][4], //. returning an object with `namespace`, `name`, and `version` fields. //. //. ```javascript //. > type.parse ('my-package/List@2') //. {namespace: 'my-package', name: 'List', version: 2} //. //. > type.parse ('nonsense!') //. {namespace: null, name: 'nonsense!', version: 0} //. //. > type.parse (type (Identity (0))) //. {namespace: 'my-package', name: 'Identity', version: 1} //. ``` type.parse = function parse(s) { var namespace = null; var name = s; var version = 0; var groups = pattern.exec (s); if (groups != null) { namespace = groups[1]; name = groups[2]; if (groups[3] != null) version = Number (groups[3]); } return {namespace: namespace, name: name, version: version}; }; return type; })); //. [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof //. [2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString //. [3]: https://docs.npmjs.com/misc/scope //. [4]: #specification }); var FL = { alt: 'fantasy-land/alt', ap: 'fantasy-land/ap', bimap: 'fantasy-land/bimap', chain: 'fantasy-land/chain', chainRec: 'fantasy-land/chainRec', map: 'fantasy-land/map', of: 'fantasy-land/of', zero: 'fantasy-land/zero' }; var ordinal = ['first', 'second', 'third', 'fourth', 'fifth']; var namespace = 'fluture'; var name = 'Future'; var version = 5; var $$type$1 = namespace + '/' + name + '@' + version; function List(head, tail){ this.head = head; this.tail = tail; } List.prototype.toJSON = function(){ return toArray(this); }; var nil = new List(null, null); nil.tail = nil; function isNil(list){ return list.tail === list; } // cons :: (a, List a) -> List a // -- O(1) append operation function cons(head, tail){ return new List(head, tail); } // reverse :: List a -> List a // -- O(n) list reversal function reverse(xs){ var ys = nil, tail = xs; while(!isNil(tail)){ ys = cons(tail.head, ys); tail = tail.tail; } return ys; } // cat :: (List a, List a) -> List a // -- O(n) list concatenation function cat(xs, ys){ var zs = ys, tail = reverse(xs); while(!isNil(tail)){ zs = cons(tail.head, zs); tail = tail.tail; } return zs; } // toArray :: List a -> Array a // -- O(n) list to Array function toArray(xs){ var tail = xs, arr = []; while(!isNil(tail)){ arr.push(tail.head); tail = tail.tail; } return arr; } /* c8 ignore next */ var captureStackTrace = Error.captureStackTrace || captureStackTraceFallback; var _debug = debugHandleNone; function debugMode(debug){ _debug = debug ? debugHandleAll : debugHandleNone; } function debugHandleNone(x){ return x; } function debugHandleAll(x, fn, a, b, c){ return fn(a, b, c); } function debug(x, fn, a, b, c){ return _debug(x, fn, a, b, c); } function captureContext(previous, tag, fn){ return debug(previous, debugCaptureContext, previous, tag, fn); } function debugCaptureContext(previous, tag, fn){ var context = {tag: tag, name: ' from ' + tag + ':'}; captureStackTrace(context, fn); return cons(context, previous); } function captureApplicationContext(context, n, f){ return debug(context, debugCaptureApplicationContext, context, n, f); } function debugCaptureApplicationContext(context, n, f){ return debugCaptureContext(context, ordinal[n - 1] + ' application of ' + f.name, f); } function captureStackTraceFallback(x){ var e = new Error; if(typeof e.stack === 'string'){ x.stack = x.name + '\n' + e.stack.split('\n').slice(1).join('\n'); /* c8 ignore next 3 */ }else { x.stack = x.name; } } var sanctuaryShow = createCommonjsModule(function (module) { //. # sanctuary-show //. //. Haskell has a `show` function which can be applied to a compatible value to //. produce a descriptive string representation of that value. The idea is that //. the string representation should, if possible, be an expression which would //. produce the original value if evaluated. //. //. This library provides a similar [`show`](#show) function. //. //. In general, this property should hold: `eval (show (x)) = x`. In some cases //. parens are necessary to ensure correct interpretation (`{}`, for example, //. is an empty block rather than an empty object in some contexts). Thus the //. property is more accurately stated `eval ('(' + show (x) + ')') = x`. //. //. One can make values of a custom type compatible with [`show`](#show) by //. defining a `@@show` method. For example: //. //. ```javascript //. //# Maybe#@@show :: Maybe a ~> () -> String //. //. //. //. ```javascript //. //. > show (Nothing) //. //. 'Nothing' //. //. //. //. > show (Just (['foo', 'bar', 'baz'])) //. //. 'Just (["foo", "bar", "baz"])' //. //. ``` //. Maybe.prototype['@@show'] = function() { //. return this.isNothing ? 'Nothing' : 'Just (' + show (this.value) + ')'; //. }; //. ``` (function(f) { /* istanbul ignore else */ { module.exports = f (); } } (function() { // $$show :: String var $$show = '@@show'; // seen :: Array Any var seen = []; // entry :: Object -> String -> String function entry(o) { return function(k) { return show (k) + ': ' + show (o[k]); }; } // sortedKeys :: Object -> Array String function sortedKeys(o) { return (Object.keys (o)).sort (); } //# show :: Showable a => a -> String //. //. Returns a useful string representation of the given value. //. //. Dispatches to the value's `@@show` method if present. //. //. Where practical, `show (eval ('(' + show (x) + ')')) = show (x)`. //. //. ```javascript //. > show (null) //. 'null' //. //. > show (undefined) //. 'undefined' //. //. > show (true) //. 'true' //. //. > show (new Boolean (false)) //. 'new Boolean (false)' //. //. > show (-0) //. '-0' //. //. > show (NaN) //. 'NaN' //. //. > show (new Number (Infinity)) //. 'new Number (Infinity)' //. //. > show ('foo\n"bar"\nbaz\n') //. '"foo\\n\\"bar\\"\\nbaz\\n"' //. //. > show (new String ('')) //. 'new String ("")' //. //. > show (['foo', 'bar', 'baz']) //. '["foo", "bar", "baz"]' //. //. > show ([[[[[0]]]]]) //. '[[[[[0]]]]]' //. //. > show ({x: [1, 2], y: [3, 4], z: [5, 6]}) //. '{"x": [1, 2], "y": [3, 4], "z": [5, 6]}' //. ``` function show(x) { if (seen.indexOf (x) >= 0) return ''; switch (Object.prototype.toString.call (x)) { case '[object Boolean]': return typeof x === 'object' ? 'new Boolean (' + show (x.valueOf ()) + ')' : x.toString (); case '[object Number]': return typeof x === 'object' ? 'new Number (' + show (x.valueOf ()) + ')' : 1 / x === -Infinity ? '-0' : x.toString (10); case '[object String]': return typeof x === 'object' ? 'new String (' + show (x.valueOf ()) + ')' : JSON.stringify (x); case '[object Date]': return 'new Date (' + show (isNaN (x.valueOf ()) ? NaN : x.toISOString ()) + ')'; case '[object Error]': return 'new ' + x.name + ' (' + show (x.message) + ')'; case '[object Arguments]': return 'function () { return arguments; } (' + (Array.prototype.map.call (x, show)).join (', ') + ')'; case '[object Array]': seen.push (x); try { return '[' + ((x.map (show)).concat ( sortedKeys (x) .filter (function(k) { return !(/^\d+$/.test (k)); }) .map (entry (x)) )).join (', ') + ']'; } finally { seen.pop (); } case '[object Object]': seen.push (x); try { return ( $$show in x && (x.constructor == null || x.constructor.prototype !== x) ? x[$$show] () : '{' + ((sortedKeys (x)).map (entry (x))).join (', ') + '}' ); } finally { seen.pop (); } case '[object Set]': seen.push (x); try { return 'new Set (' + show (Array.from (x.values ())) + ')'; } finally { seen.pop (); } case '[object Map]': seen.push (x); try { return 'new Map (' + show (Array.from (x.entries ())) + ')'; } finally { seen.pop (); } default: return String (x); } } return show; })); }); /* c8 ignore next */ var setImmediate = typeof setImmediate === 'undefined' ? setImmediateFallback : setImmediate; function noop(){} function moop(){ return this } function call(f, x){ return f(x) } function setImmediateFallback(f, x){ return setTimeout(f, 0, x); } function raise(x){ setImmediate(function rethrowErrorDelayedToEscapePromiseCatch(){ throw x; }); } function showArg$1(x){ return sanctuaryShow(x) + ' :: ' + sanctuaryTypeIdentifiers.parse(sanctuaryTypeIdentifiers(x)).name; } function error(message){ return new Error(message); } function typeError(message){ return new TypeError(message); } function invalidArgument(it, at, expected, actual){ return typeError( it + '() expects its ' + ordinal[at] + ' argument to ' + expected + '.' + '\n Actual: ' + showArg$1(actual) ); } function invalidArgumentOf(expected){ return function(it, at, actual){ return invalidArgument(it, at, expected, actual); }; } function invalidArity(f, args){ return new TypeError( f.name + '() expects to be called with a single argument per invocation\n' + ' Saw: ' + args.length + ' arguments' + Array.prototype.slice.call(args).map(function(arg, i){ return '\n ' + ( ordinal[i] ? ordinal[i].charAt(0).toUpperCase() + ordinal[i].slice(1) : 'Argument ' + String(i + 1) ) + ': ' + showArg$1(arg); }).join('') ); } function invalidNamespace(m, x){ return ( 'The Future was not created by ' + namespace + '. ' + 'Make sure you transform other Futures to ' + namespace + ' Futures. ' + 'Got ' + (x ? ('a Future from ' + x) : 'an unscoped Future') + '.' + '\n See: https://github.com/fluture-js/Fluture#casting-futures' ); } function invalidVersion(m, x){ return ( 'The Future was created by ' + (x < version ? 'an older' : 'a newer') + ' version of ' + namespace + '. ' + 'This means that one of the sources which creates Futures is outdated. ' + 'Update this source, or transform its created Futures to be compatible.' + '\n See: https://github.com/fluture-js/Fluture#casting-futures' ); } function invalidFuture(desc, m, s){ var id = sanctuaryTypeIdentifiers.parse(sanctuaryTypeIdentifiers(m)); var info = id.name === name ? '\n' + ( id.namespace !== namespace ? invalidNamespace(m, id.namespace) : id.version !== version ? invalidVersion(m, id.version) : 'Nothing seems wrong. Contact the Fluture maintainers.') : ''; return typeError( desc + ' to be a valid Future.' + info + '\n' + ' Actual: ' + sanctuaryShow(m) + ' :: ' + id.name + (s || '') ); } function invalidFutureArgument(it, at, m, s){ return invalidFuture(it + '() expects its ' + ordinal[at] + ' argument', m, s); } function ensureError(value, fn){ var message; try{ if(value instanceof Error) return value; message = 'A Non-Error was thrown from a Future: ' + sanctuaryShow(value); }catch (_){ message = 'Something was thrown from a Future, but it could not be converted to String'; } var e = error(message); captureStackTrace(e, fn); return e; } function assignUnenumerable(o, prop, value){ Object.defineProperty(o, prop, {value: value, writable: true, configurable: true}); } function wrapException(caught, callingFuture){ var origin = ensureError(caught, wrapException); var context = cat(origin.context || nil, callingFuture.context); var e = error(origin.message); assignUnenumerable(e, 'future', origin.future || callingFuture); assignUnenumerable(e, 'reason', origin.reason || origin); assignUnenumerable(e, 'stack', e.reason.stack); return withExtraContext(e, context); } function withExtraContext(e, context){ assignUnenumerable(e, 'context', context); assignUnenumerable(e, 'stack', e.stack + contextToStackTrace(context)); return e; } function contextToStackTrace(context){ var stack = '', tail = context; while(tail !== nil){ stack = stack + '\n' + tail.head.stack; tail = tail.tail; } return stack; } function isFunction(f){ return typeof f === 'function'; } function isThenable(m){ return m instanceof Promise || m != null && isFunction(m.then); } function isBoolean(f){ return typeof f === 'boolean'; } function isNumber(f){ return typeof f === 'number'; } function isUnsigned(n){ return (n === Infinity || isNumber(n) && n > 0 && n % 1 === 0); } function isObject(o){ return o !== null && typeof o === 'object'; } function isIterator(i){ return isObject(i) && isFunction(i.next); } function isArray(x){ return Array.isArray(x); } function hasMethod(method, x){ return x != null && isFunction(x[method]); } function isFunctor(x){ return hasMethod(FL.map, x); } function isAlt(x){ return isFunctor(x) && hasMethod(FL.alt, x); } function isApply(x){ return isFunctor(x) && hasMethod(FL.ap, x); } function isBifunctor(x){ return isFunctor(x) && hasMethod(FL.bimap, x); } function isChain(x){ return isApply(x) && hasMethod(FL.chain, x); } function Next(x){ return {done: false, value: x}; } function Done(x){ return {done: true, value: x}; } function isIteration(x){ return isObject(x) && isBoolean(x.done); } /*eslint no-cond-assign:0, no-constant-condition:0 */ function alwaysTrue(){ return true; } function getArgs(it){ var args = new Array(it.arity); for(var i = 1; i <= it.arity; i++){ args[i - 1] = it['$' + String(i)]; } return args; } function showArg(arg){ return ' (' + sanctuaryShow(arg) + ')'; } var any = {pred: alwaysTrue, error: invalidArgumentOf('be anything')}; var func = {pred: isFunction, error: invalidArgumentOf('be a Function')}; var future = {pred: isFuture, error: invalidFutureArgument}; var positiveInteger = {pred: isUnsigned, error: invalidArgumentOf('be a positive Integer')}; function application(n, f, type, args, prev){ if(args.length < 2 && type.pred(args[0])) return captureApplicationContext(prev, n, f); var e = args.length > 1 ? invalidArity(f, args) : type.error(f.name, n - 1, args[0]); captureStackTrace(e, f); throw withExtraContext(e, prev); } function application1(f, type, args){ return application(1, f, type, args, nil); } function Future(computation){ var context = application1(Future, func, arguments); return new Computation(context, computation); } function isFuture(x){ return x instanceof Future || sanctuaryTypeIdentifiers(x) === $$type$1; } // Compliance with sanctuary-type-identifiers versions 1 and 2. // To prevent sanctuary-type-identifiers version 3 from identifying 'Future' // as being of the type denoted by $$type, we ensure that // Future.constructor.prototype is equal to Future. Future['@@type'] = $$type$1; Future.constructor = {prototype: Future}; Future[FL.of] = resolve; Future[FL.chainRec] = chainRec; Future.prototype['@@type'] = $$type$1; Future.prototype['@@show'] = function Future$show(){ return this.toString(); }; Future.prototype.pipe = function Future$pipe(f){ if(!isFunction(f)) throw invalidArgument('Future#pipe', 0, 'be a Function', f); return f(this); }; Future.prototype[FL.ap] = function Future$FL$ap(other){ var context = captureContext(nil, 'a Fantasy Land dispatch to ap', Future$FL$ap); return other._transform(new ApTransformation(context, this)); }; Future.prototype[FL.map] = function Future$FL$map(mapper){ var context = captureContext(nil, 'a Fantasy Land dispatch to map', Future$FL$map); return this._transform(new MapTransformation(context, mapper)); }; Future.prototype[FL.bimap] = function Future$FL$bimap(lmapper, rmapper){ var context = captureContext(nil, 'a Fantasy Land dispatch to bimap', Future$FL$bimap); return this._transform(new BimapTransformation(context, lmapper, rmapper)); }; Future.prototype[FL.chain] = function Future$FL$chain(mapper){ var context = captureContext(nil, 'a Fantasy Land dispatch to chain', Future$FL$chain); return this._transform(new ChainTransformation(context, mapper)); }; Future.prototype[FL.alt] = function Future$FL$alt(other){ var context = captureContext(nil, 'a Fantasy Land dispatch to alt', Future$FL$alt); return this._transform(new AltTransformation(context, other)); }; Future.prototype.extractLeft = function Future$extractLeft(){ return []; }; Future.prototype.extractRight = function Future$extractRight(){ return []; }; Future.prototype._transform = function Future$transform(transformation){ return new Transformer(transformation.context, this, cons(transformation, nil)); }; Future.prototype.isTransformer = false; Future.prototype.context = nil; Future.prototype.arity = 0; Future.prototype.name = 'future'; Future.prototype.toString = function Future$toString(){ return this.name + getArgs(this).map(showArg).join(''); }; Future.prototype.toJSON = function Future$toJSON(){ return {$: $$type$1, kind: 'interpreter', type: this.name, args: getArgs(this)}; }; function createInterpreter(arity, name, interpret){ var Interpreter = function(context, $1, $2, $3){ this.context = context; this.$1 = $1; this.$2 = $2; this.$3 = $3; }; Interpreter.prototype = Object.create(Future.prototype); Interpreter.prototype.arity = arity; Interpreter.prototype.name = name; Interpreter.prototype._interpret = interpret; return Interpreter; } var Computation = createInterpreter(1, 'Future', function Computation$interpret(rec, rej, res){ var computation = this.$1, open = false, cancel = noop, cont = function(){ open = true; }; try{ cancel = computation(function Computation$rej(x){ cont = function Computation$rej$cont(){ open = false; rej(x); }; if(open){ cont(); } }, function Computation$res(x){ cont = function Computation$res$cont(){ open = false; res(x); }; if(open){ cont(); } }); }catch(e){ rec(wrapException(e, this)); return noop; } if(!(isFunction(cancel) && cancel.length === 0)){ rec(wrapException(typeError( 'The computation was expected to return a nullary cancellation function\n' + ' Actual: ' + sanctuaryShow(cancel) ), this)); return noop; } cont(); return function Computation$cancel(){ if(open){ open = false; cancel && cancel(); } }; }); var Never = createInterpreter(0, 'never', function Never$interpret(){ return noop; }); Never.prototype._isNever = true; var never = new Never(nil); function isNever(x){ return isFuture(x) && x._isNever === true; } var Crash = createInterpreter(1, 'crash', function Crash$interpret(rec){ rec(this.$1); return noop; }); function crash(x){ return new Crash(application1(crash, any, arguments), x); } var Reject = createInterpreter(1, 'reject', function Reject$interpret(rec, rej){ rej(this.$1); return noop; }); Reject.prototype.extractLeft = function Reject$extractLeft(){ return [this.$1]; }; function reject(x){ return new Reject(application1(reject, any, arguments), x); } var Resolve = createInterpreter(1, 'resolve', function Resolve$interpret(rec, rej, res){ res(this.$1); return noop; }); Resolve.prototype.extractRight = function Resolve$extractRight(){ return [this.$1]; }; function resolve(x){ return new Resolve(application1(resolve, any, arguments), x); } //Note: This function is not curried because it's only used to satisfy the // Fantasy Land ChainRec specification. function chainRec(step, init){ return resolve(Next(init))._transform(new ChainTransformation(nil, function chainRec$recur(o){ return o.done ? resolve(o.value) : step(Next, Done, o.value)._transform(new ChainTransformation(nil, chainRec$recur)); })); } var Transformer = createInterpreter(2, 'transform', function Transformer$interpret(rec, rej, res){ //These are the cold, and hot, transformation stacks. The cold actions are those that //have yet to run parallel computations, and hot are those that have. var cold = nil, hot = nil; //These combined variables define our current state. // future = the future we are currently forking // transformation = the transformation to be informed when the future settles // cancel = the cancel function of the current future // settled = a boolean indicating whether a new tick should start // async = a boolean indicating whether we are awaiting a result asynchronously var future, transformation, cancel = noop, settled, async = true, it; //Takes a transformation from the top of the hot stack and returns it. function nextHot(){ var x = hot.head; hot = hot.tail; return x; } //Takes a transformation from the top of the cold stack and returns it. function nextCold(){ var x = cold.head; cold = cold.tail; return x; } //This function is called with a future to use in the next tick. //Here we "flatten" the actions of another Sequence into our own actions, //this is the magic that allows for infinitely stack safe recursion because //actions like ChainAction will return a new Sequence. //If we settled asynchronously, we call drain() directly to run the next tick. function settle(m){ settled = true; future = m; if(future.isTransformer){ var tail = future.$2; while(!isNil(tail)){ cold = cons(tail.head, cold); tail = tail.tail; } future = future.$1; } if(async) drain(); } //This function serves as a rejection handler for our current future. //It will tell the current transformation that the future rejected, and it will //settle the current tick with the transformation's answer to that. function rejected(x){ settle(transformation.rejected(x)); } //This function serves as a resolution handler for our current future. //It will tell the current transformation that the future resolved, and it will //settle the current tick with the transformation's answer to that. function resolved(x){ settle(transformation.resolved(x)); } //This function is passed into actions when they are "warmed up". //If the transformation decides that it has its result, without the need to await //anything else, then it can call this function to force "early termination". //When early termination occurs, all actions which were stacked prior to the //terminator will be skipped. If they were already hot, they will also be //sent a cancel signal so they can cancel their own concurrent computations, //as their results are no longer needed. function early(m, terminator){ cancel(); cold = nil; if(async && transformation !== terminator){ transformation.cancel(); while((it = nextHot()) && it !== terminator) it.cancel(); } settle(m); } //This will cancel the current Future, the current transformation, and all stacked hot actions. function Sequence$cancel(){ cancel(); transformation && transformation.cancel(); while(it = nextHot()) it.cancel(); } //This function is called when an exception is caught. function exception(e){ Sequence$cancel(); settled = true; cold = hot = nil; var error = wrapException(e, future); future = never; rec(error); } //This function serves to kickstart concurrent computations. //Takes all actions from the cold stack in reverse order, and calls run() on //each of them, passing them the "early" function. If any of them settles (by //calling early()), we abort. After warming up all actions in the cold queue, //we warm up the current transformation as well. function warmupActions(){ cold = reverse(cold); while(cold !== nil){ it = cold.head.run(early); if(settled) return; hot = cons(it, hot); cold = cold.tail; } transformation = transformation.run(early); } //This function represents our main execution loop. By "tick", we've been //referring to the execution of one iteration in the while-loop below. function drain(){ async = false; while(true){ settled = false; if(transformation = nextCold()){ cancel = future._interpret(exception, rejected, resolved); if(!settled) warmupActions(); }else if(transformation = nextHot()){ cancel = future._interpret(exception, rejected, resolved); }else break; if(settled) continue; async = true; return; } cancel = future._interpret(exception, rej, res); } //Start the execution loop. settle(this); //Return the cancellation function. return Sequence$cancel; }); Transformer.prototype.isTransformer = true; Transformer.prototype._transform = function Transformer$_transform(transformation){ return new Transformer(transformation.context, this.$1, cons(transformation, this.$2)); }; Transformer.prototype.toString = function Transformer$toString(){ return toArray(reverse(this.$2)).reduce(function(str, action){ return action.name + getArgs(action).map(showArg).join('') + ' (' + str + ')'; }, this.$1.toString()); }; function BaseTransformation$rejected(x){ this.cancel(); return new Reject(this.context, x); } function BaseTransformation$resolved(x){ this.cancel(); return new Resolve(this.context, x); } function BaseTransformation$toJSON(){ return {$: $$type$1, kind: 'transformation', type: this.name, args: getArgs(this)}; } var BaseTransformation = { rejected: BaseTransformation$rejected, resolved: BaseTransformation$resolved, run: moop, cancel: noop, context: nil, arity: 0, name: 'transform', toJSON: BaseTransformation$toJSON }; function wrapHandler(handler){ return function transformationHandler(x){ var m; try{ m = handler.call(this, x); }catch(e){ return new Crash(this.context, e); } if(isFuture(m)){ return m; } return new Crash(this.context, invalidFuture( this.name + ' expects the return value from the function it\'s given', m, '\n When called with: ' + sanctuaryShow(x) )); }; } function createTransformation(arity, name, prototype){ var Transformation = function(context, $1, $2){ this.context = context; this.$1 = $1; this.$2 = $2; }; Transformation.prototype = Object.create(BaseTransformation); Transformation.prototype.arity = arity; Transformation.prototype.name = name; if(typeof prototype.rejected === 'function'){ Transformation.prototype.rejected = wrapHandler(prototype.rejected); } if(typeof prototype.resolved === 'function'){ Transformation.prototype.resolved = wrapHandler(prototype.resolved); } if(typeof prototype.run === 'function'){ Transformation.prototype.run = prototype.run; } return Transformation; } var ApTransformation = createTransformation(1, 'ap', { resolved: function ApTransformation$resolved(f){ if(isFunction(f)) return this.$1._transform(new MapTransformation(this.context, f)); throw typeError( 'ap expects the second Future to resolve to a Function\n' + ' Actual: ' + sanctuaryShow(f) ); } }); var AltTransformation = createTransformation(1, 'alt', { rejected: function AltTransformation$rejected(){ return this.$1 } }); var MapTransformation = createTransformation(1, 'map', { resolved: function MapTransformation$resolved(x){ return new Resolve(this.context, call(this.$1, x)); } }); var BimapTransformation = createTransformation(2, 'bimap', { rejected: function BimapTransformation$rejected(x){ return new Reject(this.context, call(this.$1, x)); }, resolved: function BimapTransformation$resolved(x){ return new Resolve(this.context, call(this.$2, x)); } }); var ChainTransformation = createTransformation(1, 'chain', { resolved: function ChainTransformation$resolved(x){ return call(this.$1, x) } }); var After = createInterpreter(2, 'after', function After$interpret(rec, rej, res){ var id = setTimeout(res, this.$1, this.$2); return function After$cancel(){ clearTimeout(id); }; }); After.prototype.extractRight = function After$extractRight(){ return [this.$2]; }; function alwaysNever$1(_){ return never; } function after(time){ var context1 = application1(after, positiveInteger, arguments); return time === Infinity ? alwaysNever$1 : (function after(value){ var context2 = application(2, after, any, arguments, context1); return new After(context2, time, value); }); } var alternative = {pred: isAlt, error: invalidArgumentOf('have Alt implemented')}; function alt(left){ if(isFuture(left)){ var context1 = application1(alt, future, arguments); return function alt(right){ var context2 = application(2, alt, future, arguments, context1); return right._transform(new AltTransformation(context2, left)); }; } var context = application1(alt, alternative, arguments); return function alt(right){ application(2, alt, alternative, arguments, context); return left[FL.alt](right); }; } var AndTransformation = createTransformation(1, 'and', { resolved: function AndTransformation$resolved(){ return this.$1 } }); function and(left){ var context1 = application1(and, future, arguments); return function and(right){ var context2 = application(2, and, future, arguments, context1); return right._transform(new AndTransformation(context2, left)); }; } var apply = {pred: isApply, error: invalidArgumentOf('have Apply implemented')}; function ap(mx){ if(isFuture(mx)){ var context1 = application1(ap, future, arguments); return function ap(mf){ var context2 = application(2, ap, future, arguments, context1); return mf._transform(new ApTransformation(context2, mx)); }; } var context = application1(ap, apply, arguments); return function ap(mf){ application(2, ap, apply, arguments, context); return mx[FL.ap](mf); }; } function invalidPromise(p, f, a){ return typeError( 'encaseP() expects the function it\'s given to return a Promise/Thenable' + '\n Actual: ' + sanctuaryShow(p) + '\n From calling: ' + sanctuaryShow(f) + '\n With: ' + sanctuaryShow(a) ); } var EncaseP = createInterpreter(2, 'encaseP', function EncaseP$interpret(rec, rej, res){ var open = true, fn = this.$1, arg = this.$2, p; try{ p = fn(arg); }catch(e){ rec(wrapException(e, this)); return noop; } if(!isThenable(p)){ rec(wrapException(invalidPromise(p, fn, arg), this)); return noop; } p.then(function EncaseP$res(x){ if(open){ open = false; res(x); } }, function EncaseP$rej(x){ if(open){ open = false; rej(x); } }); return function EncaseP$cancel(){ open = false; }; }); function encaseP(f){ var context1 = application1(encaseP, func, arguments); return function encaseP(x){ var context2 = application(2, encaseP, any, arguments, context1); return new EncaseP(context2, f, x); }; } function attemptP(_){ return encaseP.apply(this, arguments)(undefined); } var Encase = createInterpreter(2, 'encase', function Encase$interpret(rec, rej, res){ var fn = this.$1, r; try{ r = fn(this.$2); }catch(e){ rej(e); return noop } res(r); return noop; }); function encase(f){ var context1 = application1(encase, func, arguments); return function encase(x){ var context2 = application(2, encase, any, arguments, context1); return new Encase(context2, f, x); }; } function attempt(_){ return encase.apply(this, arguments)(undefined); } var bifunctor = {pred: isBifunctor, error: invalidArgumentOf('have Bifunctor implemented')}; function bimap(f){ var context1 = application1(bimap, func, arguments); return function bimap(g){ var context2 = application(2, bimap, func, arguments, context1); return function bimap(m){ var context3 = application(3, bimap, bifunctor, arguments, context2); return isFuture(m) ? m._transform(new BimapTransformation(context3, f, g)) : m[FL.bimap](f, g); }; }; } var BichainTransformation = createTransformation(2, 'bichain', { rejected: function BichainTransformation$rejected(x){ return call(this.$1, x) }, resolved: function BichainTransformation$resolved(x){ return call(this.$2, x) } }); function bichain(f){ var context1 = application1(bichain, func, arguments); return function bichain(g){ var context2 = application(2, bichain, func, arguments, context1); return function bichain(m){ var context3 = application(3, bichain, future, arguments, context2); return m._transform(new BichainTransformation(context3, f, g)); }; }; } function Eager(future){ var _this = this; _this.rec = noop; _this.rej = noop; _this.res = noop; _this.crashed = false; _this.rejected = false; _this.resolved = false; _this.value = null; _this.cancel = future._interpret(function Eager$crash(x){ _this.value = x; _this.crashed = true; _this.cancel = noop; _this.rec(x); }, function Eager$reject(x){ _this.value = x; _this.rejected = true; _this.cancel = noop; _this.rej(x); }, function Eager$resolve(x){ _this.value = x; _this.resolved = true; _this.cancel = noop; _this.res(x); }); } Eager.prototype = Object.create(Future.prototype); Eager.prototype._interpret = function Eager$interpret(rec, rej, res){ if(this.crashed) rec(this.value); else if(this.rejected) rej(this.value); else if(this.resolved) res(this.value); else { this.rec = rec; this.rej = rej; this.res = res; } return this.cancel; }; function earlyCrash(early, x){ early(crash(x)); } function earlyReject(early, x){ early(reject(x)); } function earlyResolve(early, x){ early(resolve(x)); } function createParallelTransformation(name, rec, rej, res, prototype){ var ParallelTransformation = createTransformation(1, name, Object.assign({ run: function Parallel$run(early){ var eager = new Eager(this.$1); var transformation = new ParallelTransformation(this.context, eager); function Parallel$early(m){ early(m, transformation); } transformation.cancel = eager._interpret( function Parallel$rec(x){ rec(Parallel$early, x); }, function Parallel$rej(x){ rej(Parallel$early, x); }, function Parallel$res(x){ res(Parallel$early, x); } ); return transformation; } }, prototype)); return ParallelTransformation; } var PairTransformation = createTransformation(1, 'pair', { resolved: function PairTransformation$resolved(x){ return new Resolve(this.context, [x, this.$1]); } }); var BothTransformation = createParallelTransformation('both', earlyCrash, earlyReject, noop, { resolved: function BothTransformation$resolved(x){ return this.$1._transform(new PairTransformation(this.context, x)); } }); function both(left){ var context1 = application1(both, future, arguments); return function both(right){ var context2 = application(2, both, future, arguments, context1); return right._transform(new BothTransformation(context2, left)); }; } var Cold = 0; var Pending = 1; var Crashed = 2; var Rejected = 3; var Resolved = 4; function Queued(rec, rej, res){ this[Crashed] = rec; this[Rejected] = rej; this[Resolved] = res; } var Cache = createInterpreter(1, 'cache', function Cache$interpret(rec, rej, res){ var cancel = noop; switch(this._state){ /* c8 ignore next 4 */ case Pending: cancel = this._addToQueue(rec, rej, res); break; case Crashed: rec(this._value); break; case Rejected: rej(this._value); break; case Resolved: res(this._value); break; default: this._queue = []; cancel = this._addToQueue(rec, rej, res); this.run(); } return cancel; }); Cache.prototype._cancel = noop; Cache.prototype._queue = null; Cache.prototype._queued = 0; Cache.prototype._value = undefined; Cache.prototype._state = Cold; Cache.prototype.extractLeft = function Cache$extractLeft(){ return this._state === Rejected ? [this._value] : []; }; Cache.prototype.extractRight = function Cache$extractRight(){ return this._state === Resolved ? [this._value] : []; }; Cache.prototype._addToQueue = function Cache$addToQueue(rec, rej, res){ var _this = this; if(_this._state > Pending) return noop; var i = _this._queue.push(new Queued(rec, rej, res)) - 1; _this._queued = _this._queued + 1; return function Cache$removeFromQueue(){ if(_this._state > Pending) return; _this._queue[i] = undefined; _this._queued = _this._queued - 1; if(_this._queued === 0) _this.reset(); }; }; Cache.prototype._drainQueue = function Cache$drainQueue(){ if(this._state <= Pending) return; if(this._queued === 0) return; var queue = this._queue; var length = queue.length; var state = this._state; var value = this._value; for(var i = 0; i < length; i++){ queue[i] && queue[i][state](value); queue[i] = undefined; } this._queue = undefined; this._queued = 0; }; Cache.prototype.crash = function Cache$crash(error){ if(this._state > Pending) return; this._value = error; this._state = Crashed; this._drainQueue(); }; Cache.prototype.reject = function Cache$reject(reason){ if(this._state > Pending) return; this._value = reason; this._state = Rejected; this._drainQueue(); }; Cache.prototype.resolve = function Cache$resolve(value){ if(this._state > Pending) return; this._value = value; this._state = Resolved; this._drainQueue(); }; Cache.prototype.run = function Cache$run(){ var _this = this; if(_this._state > Cold) return; _this._state = Pending; _this._cancel = _this.$1._interpret( function Cache$fork$rec(x){ _this.crash(x); }, function Cache$fork$rej(x){ _this.reject(x); }, function Cache$fork$res(x){ _this.resolve(x); } ); }; Cache.prototype.reset = function Cache$reset(){ if(this._state === Cold) return; if(this._state === Pending) this._cancel(); this._cancel = noop; this._queue = []; this._queued = 0; this._value = undefined; this._state = Cold; }; function cache(m){ return new Cache(application1(cache, future, arguments), m); } var ChainRejTransformation = createTransformation(1, 'chainRej', { rejected: function ChainRejTransformation$rejected(x){ return call(this.$1, x) } }); function chainRej(f){ var context1 = application1(chainRej, func, arguments); return function chainRej(m){ var context2 = application(2, chainRej, future, arguments, context1); return m._transform(new ChainRejTransformation(context2, f)); }; } var monad = {pred: isChain, error: invalidArgumentOf('have Chain implemented')}; function chain(f){ var context1 = application1(chain, func, arguments); return function chain(m){ var context2 = application(2, chain, monad, arguments, context1); return isFuture(m) ? m._transform(new ChainTransformation(context2, f)) : m[FL.chain](f); }; } function done(callback){ var context1 = application1(done, func, arguments); function done$res(x){ callback(null, x); } return function done(m){ application(2, done, future, arguments, context1); return m._interpret(raise, callback, done$res); }; } function extractLeft(m){ application1(extractLeft, future, arguments); return m.extractLeft(); } function extractRight(m){ application1(extractRight, future, arguments); return m.extractRight(); } var CoalesceTransformation = createTransformation(2, 'coalesce', { rejected: function CoalesceTransformation$rejected(x){ return new Resolve(this.context, call(this.$1, x)); }, resolved: function CoalesceTransformation$resolved(x){ return new Resolve(this.context, call(this.$2, x)); } }); function coalesce(f){ var context1 = application1(coalesce, func, arguments); return function coalesce(g){ var context2 = application(2, coalesce, func, arguments, context1); return function coalesce(m){ var context3 = application(3, coalesce, future, arguments, context2); return m._transform(new CoalesceTransformation(context3, f, g)); }; }; } function forkCatch(f){ var context1 = application1(forkCatch, func, arguments); return function forkCatch(g){ var context2 = application(2, forkCatch, func, arguments, context1); return function forkCatch(h){ var context3 = application(3, forkCatch, func, arguments, context2); return function forkCatch(m){ application(4, forkCatch, future, arguments, context3); return m._interpret(f, g, h); }; }; }; } function fork(f){ var context1 = application1(fork, func, arguments); return function fork(g){ var context2 = application(2, fork, func, arguments, context1); return function fork(m){ application(3, fork, future, arguments, context2); return m._interpret(raise, f, g); }; }; } var Undetermined = 0; var Synchronous = 1; var Asynchronous = 2; /*eslint consistent-return: 0 */ function invalidIteration(o){ return typeError( 'The iterator did not return a valid iteration from iterator.next()\n' + ' Actual: ' + sanctuaryShow(o) ); } function invalidState(x){ return invalidFuture( 'go() expects the value produced by the iterator', x, '\n Tip: If you\'re using a generator, make sure you always yield a Future' ); } var Go = createInterpreter(1, 'go', function Go$interpret(rec, rej, res){ var _this = this, timing = Undetermined, cancel = noop, state, value, iterator; function crash(e){ rec(wrapException(e, _this)); } try{ iterator = _this.$1(); }catch(e){ crash(e); return noop; } if(!isIterator(iterator)){ crash(invalidArgument('go', 0, 'return an iterator, maybe you forgot the "*"', iterator)); return noop; } function resolved(x){ value = x; if(timing === Asynchronous) return drain(); timing = Synchronous; } function drain(){ //eslint-disable-next-line no-constant-condition while(true){ try{ state = iterator.next(value); }catch(e){ return crash(e); } if(!isIteration(state)) return crash(invalidIteration(state)); if(state.done) break; if(!isFuture(state.value)){ return crash(invalidState(state.value)); } timing = Undetermined; cancel = state.value._interpret(crash, rej, resolved); if(timing === Undetermined) return timing = Asynchronous; } res(state.value); } drain(); return function Go$cancel(){ cancel(); }; }); function go(generator){ return new Go(application1(go, func, arguments), generator); } function invalidDisposal(m, f, x){ return invalidFuture( 'hook() expects the return value from the first function it\'s given', m, '\n From calling: ' + sanctuaryShow(f) + '\n With: ' + sanctuaryShow(x) ); } function invalidConsumption(m, f, x){ return invalidFuture( 'hook() expects the return value from the second function it\'s given', m, '\n From calling: ' + sanctuaryShow(f) + '\n With: ' + sanctuaryShow(x) ); } var Hook = createInterpreter(3, 'hook', function Hook$interpret(rec, rej, res){ var _this = this, _acquire = this.$1, _dispose = this.$2, _consume = this.$3; var cancel, cancelConsume = noop, resource, value, cont = noop; function Hook$done(){ cont(value); } function Hook$rec(x){ rec(wrapException(x, _this)); } function Hook$dispose(){ var disposal; try{ disposal = _dispose(resource); }catch(e){ return Hook$rec(e); } if(!isFuture(disposal)){ return Hook$rec(invalidDisposal(disposal, _dispose, resource)); } cancel = Hook$cancelDisposal; disposal._interpret(Hook$rec, Hook$disposalRejected, Hook$done); } function Hook$cancelConsumption(){ cancelConsume(); Hook$dispose(); Hook$cancelDisposal(); } function Hook$cancelDisposal(){ cont = noop; } function Hook$disposalRejected(x){ Hook$rec(new Error('The disposal Future rejected with ' + sanctuaryShow(x))); } function Hook$consumptionException(x){ cont = Hook$rec; value = x; Hook$dispose(); } function Hook$consumptionRejected(x){ cont = rej; value = x; Hook$dispose(); } function Hook$consumptionResolved(x){ cont = res; value = x; Hook$dispose(); } function Hook$consume(x){ resource = x; var consumption; try{ consumption = _consume(resource); }catch(e){ return Hook$consumptionException(e); } if(!isFuture(consumption)){ return Hook$consumptionException(invalidConsumption(consumption, _consume, resource)); } cancel = Hook$cancelConsumption; cancelConsume = consumption._interpret( Hook$consumptionException, Hook$consumptionRejected, Hook$consumptionResolved ); } var cancelAcquire = _acquire._interpret(Hook$rec, rej, Hook$consume); cancel = cancel || cancelAcquire; return function Hook$fork$cancel(){ rec = raise; cancel(); }; }); function hook(acquire){ var context1 = application1(hook, future, arguments); return function hook(dispose){ var context2 = application(2, hook, func, arguments, context1); return function hook(consume){ var context3 = application(3, hook, func, arguments, context2); return new Hook(context3, acquire, dispose, consume); }; }; } var LastlyTransformation = createTransformation(1, 'lastly', { rejected: function LastlyAction$rejected(x){ return this.$1._transform(new AndTransformation(this.context, new Reject(this.context, x))); }, resolved: function LastlyAction$resolved(x){ return this.$1._transform(new AndTransformation(this.context, new Resolve(this.context, x))); } }); function lastly(cleanup){ var context1 = application1(lastly, future, arguments); return function lastly(program){ var context2 = application(2, lastly, future, arguments, context1); return program._transform(new LastlyTransformation(context2, cleanup)); }; } var MapRejTransformation = createTransformation(1, 'mapRej', { rejected: function MapRejTransformation$rejected(x){ return new Reject(this.context, call(this.$1, x)); } }); function mapRej(f){ var context1 = application1(mapRej, func, arguments); return function mapRej(m){ var context2 = application(2, mapRej, future, arguments, context1); return m._transform(new MapRejTransformation(context2, f)); }; } var functor = {pred: isFunctor, error: invalidArgumentOf('have Functor implemented')}; function map(f){ var context1 = application1(map, func, arguments); return function map(m){ var context2 = application(2, map, functor, arguments, context1); return isFuture(m) ? m._transform(new MapTransformation(context2, f)) : m[FL.map](f); }; } var Node = createInterpreter(1, 'node', function Node$interpret(rec, rej, res){ function Node$done(err, val){ cont = err ? function EncaseN3$rej(){ open = false; rej(err); } : function EncaseN3$res(){ open = false; res(val); }; if(open){ cont(); } } var open = false, cont = function(){ open = true; }; try{ call(this.$1, Node$done); }catch(e){ rec(wrapException(e, this)); open = false; return noop; } cont(); return function Node$cancel(){ open = false; }; }); function node(f){ return new Node(application1(node, func, arguments), f); } var ParallelApTransformation = createParallelTransformation('pap', earlyCrash, earlyReject, noop, { resolved: function ParallelApTransformation$resolved(f){ if(isFunction(f)) return this.$1._transform(new MapTransformation(this.context, f)); throw typeError( 'pap expects the second Future to resolve to a Function\n' + ' Actual: ' + sanctuaryShow(f) ); } }); function pap(mx){ var context1 = application1(pap, future, arguments); return function pap(mf){ var context2 = application(2, pap, future, arguments, context1); return mf._transform(new ParallelApTransformation(context2, mx)); }; } function isFutureArray(xs){ if(!isArray(xs)) return false; for(var i = 0; i < xs.length; i++){ if(!isFuture(xs[i])) return false; } return true; } var futureArray = { pred: isFutureArray, error: invalidArgumentOf('be an Array of valid Futures') }; var Parallel = createInterpreter(2, 'parallel', function Parallel$interpret(rec, rej, res){ var _this = this, futures = this.$2, length = futures.length; var max = Math.min(this.$1, length), cancels = new Array(length), out = new Array(length); var cursor = 0, running = 0, blocked = false, cont = noop; function Parallel$cancel(){ rec = noop; rej = noop; res = noop; cursor = length; for(var n = 0; n < length; n++) cancels[n] && cancels[n](); } function Parallel$run(idx){ running++; cancels[idx] = futures[idx]._interpret(function Parallel$rec(e){ cont = rec; cancels[idx] = noop; Parallel$cancel(); cont(wrapException(e, _this)); }, function Parallel$rej(reason){ cont = rej; cancels[idx] = noop; Parallel$cancel(); cont(reason); }, function Parallel$res(value){ cancels[idx] = noop; out[idx] = value; running--; if(cursor === length && running === 0) res(out); else if(blocked) Parallel$drain(); }); } function Parallel$drain(){ blocked = false; while(cursor < length && running < max) Parallel$run(cursor++); blocked = true; } Parallel$drain(); return Parallel$cancel; }); var emptyArray = resolve([]); function parallel$1(max){ var context1 = application1(parallel$1, positiveInteger, arguments); return function parallel(ms){ var context2 = application(2, parallel, futureArray, arguments, context1); return ms.length === 0 ? emptyArray : new Parallel(context2, max, ms); }; } var RaceTransformation = createParallelTransformation('race', earlyCrash, earlyReject, earlyResolve, {}); function race(left){ var context1 = application1(race, future, arguments); return function race(right){ var context2 = application(2, race, future, arguments, context1); return right._transform(new RaceTransformation(context2, left)); }; } function ConcurrentFuture (sequential){ this.sequential = sequential; } ConcurrentFuture.prototype = Object.create(Par.prototype); function Par (sequential){ if(!isFuture(sequential)) throw invalidFutureArgument(Par.name, 0, sequential); return new ConcurrentFuture(sequential); } var $$type = namespace + '/ConcurrentFuture@' + version; var zeroInstance = new ConcurrentFuture(never); // Compliance with sanctuary-type-identifiers versions 1 and 2. // To prevent sanctuary-type-identifiers version 3 from identifying // 'Par' as being of the type denoted by $$type, we ensure that // Par.constructor.prototype is equal to Par. Par['@@type'] = $$type; Par.constructor = {prototype: Par}; Par[FL.of] = function Par$of(x){ return new ConcurrentFuture(resolve(x)); }; Par[FL.zero] = function Par$zero(){ return zeroInstance; }; Par.prototype['@@type'] = $$type; Par.prototype['@@show'] = function Par$show(){ return this.toString(); }; Par.prototype.toString = function Par$toString(){ return 'Par (' + this.sequential.toString() + ')'; }; Par.prototype[FL.map] = function Par$FL$map(f){ var context = captureContext( nil, 'a Fantasy Land dispatch to map via ConcurrentFuture', Par$FL$map ); return new ConcurrentFuture(this.sequential._transform(new MapTransformation(context, f))); }; Par.prototype[FL.ap] = function Par$FL$ap(other){ var context = captureContext( nil, 'a Fantasy Land dispatch to ap via ConcurrentFuture', Par$FL$ap ); return new ConcurrentFuture(other.sequential._transform( new ParallelApTransformation(context, this.sequential) )); }; Par.prototype[FL.alt] = function Par$FL$alt(other){ var context = captureContext( nil, 'a Fantasy Land dispatch to alt via ConcurrentFuture', Par$FL$alt ); return new ConcurrentFuture(other.sequential._transform( new RaceTransformation(context, this.sequential) )); }; function isParallel(x){ return x instanceof ConcurrentFuture || sanctuaryTypeIdentifiers(x) === $$type; } function promise(m){ application1(promise, future, arguments); return new Promise(function promise$computation(res, rej){ m._interpret(rej, rej, res); }); } var RejectAfter = createInterpreter(2, 'rejectAfter', function RejectAfter$interpret(rec, rej){ var id = setTimeout(rej, this.$1, this.$2); return function RejectAfter$cancel(){ clearTimeout(id); }; }); RejectAfter.prototype.extractLeft = function RejectAfter$extractLeft(){ return [this.$2]; }; function alwaysNever(_){ return never; } function rejectAfter(time){ var context1 = application1(rejectAfter, positiveInteger, arguments); return time === Infinity ? alwaysNever : (function rejectAfter(value){ var context2 = application(2, rejectAfter, any, arguments, context1); return new RejectAfter(context2, time, value); }); } var parallel = {pred: isParallel, error: invalidArgumentOf('be a ConcurrentFuture')}; function seq(par){ application1(seq, parallel, arguments); return par.sequential; } var SwapTransformation = createTransformation(0, 'swap', { resolved: function SwapTransformation$resolved(x){ return new Reject(this.context, x); }, rejected: function SwapTransformation$rejected(x){ return new Resolve(this.context, x); } }); function swap(m){ var context = application1(swap, future, arguments); return m._transform(new SwapTransformation(context)); } function value(res){ var context1 = application1(value, func, arguments); return function value(m){ application(2, value, future, arguments, context1); function value$rej(x){ raise(error( 'Future#value was called on a rejected Future\n' + ' Rejection: ' + sanctuaryShow(x) + '\n' + ' Future: ' + sanctuaryShow(m) )); } return m._interpret(raise, value$rej, res); }; } export default Future; export { Future, Par, after, alt, and, ap, attempt, attemptP, bichain, bimap, both, cache, chain, chainRej, coalesce, debugMode, done, encase, encaseP, extractLeft, extractRight, fork, forkCatch, go, hook, isFuture, isNever, lastly, map, mapRej, never, node, pap, parallel$1 as parallel, promise, race, reject, rejectAfter, resolve, seq, swap, value }; /** Fluture license The MIT License (MIT) Copyright (c) 2020 Aldwin Vlasblom Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** sanctuary-show license The MIT License (MIT) Copyright (c) 2020 Sanctuary Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** sanctuary-type-identifiers license The MIT License (MIT) Copyright (c) 2019 Sanctuary Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ ================================================ FILE: index.cjs.js ================================================ import * as Fluture from './index.js'; export default Object.assign(Fluture.Future, Fluture); ================================================ FILE: index.d.ts ================================================ /// /// export interface RecoverFunction { (exception: Error): void } export interface RejectFunction { (error: L): void } export interface ResolveFunction { (value: R): void } export interface Cancel { (): void } export interface Nodeback { (err: E | null, value?: R): void } declare const $T: unique symbol export interface Functor { [$T]: unknown 'fantasy-land/map'(mapper: (value: A) => B): Functor } type Mapped, B> = ReturnType<(F & { [$T]: B })['fantasy-land/map']> export interface ConcurrentFutureInstance extends Functor { sequential: FutureInstance 'fantasy-land/ap'(this: ConcurrentFutureInstance B>, right: ConcurrentFutureInstance): ConcurrentFutureInstance 'fantasy-land/map'(mapper: (value: R) => RB): ConcurrentFutureInstance 'fantasy-land/alt'(right: ConcurrentFutureInstance): ConcurrentFutureInstance } export interface FutureInstance extends Functor { /** The Future constructor */ constructor: FutureTypeRep /** Apply a function to this Future. See https://github.com/fluture-js/Fluture#pipe */ pipe(fn: (future: this) => T): T /** Attempt to extract the rejection reason. See https://github.com/fluture-js/Fluture#extractleft */ extractLeft(): Array /** Attempt to extract the resolution value. See https://github.com/fluture-js/Fluture#extractright */ extractRight(): Array 'fantasy-land/ap'(this: FutureInstance B>, right: FutureInstance): FutureInstance 'fantasy-land/map'(mapper: (value: R) => RB): FutureInstance 'fantasy-land/alt'(right: FutureInstance): FutureInstance 'fantasy-land/bimap'(lmapper: (reason: L) => LB, rmapper: (value: R) => RB): FutureInstance 'fantasy-land/chain'(mapper: (value: R) => FutureInstance): FutureInstance } /** Creates a Future which resolves after the given duration with the given value. See https://github.com/fluture-js/Fluture#after */ export function after(duration: number): (value: R) => FutureInstance /** Logical and for Futures. See https://github.com/fluture-js/Fluture#and */ export function and(left: FutureInstance): (right: FutureInstance) => FutureInstance /** Logical or for Futures. See https://github.com/fluture-js/Fluture#alt */ export function alt(left: FutureInstance): (right: FutureInstance) => FutureInstance /** Race two ConcurrentFutures. See https://github.com/fluture-js/Fluture#alt */ export function alt(left: ConcurrentFutureInstance): (right: ConcurrentFutureInstance) => ConcurrentFutureInstance /** Apply the function in the right Future to the value in the left Future. See https://github.com/fluture-js/Fluture#ap */ export function ap(value: FutureInstance): (apply: FutureInstance RB>) => FutureInstance /** Apply the function in the right ConcurrentFuture to the value in the left ConcurrentFuture. See https://github.com/fluture-js/Fluture#ap */ export function ap(value: ConcurrentFutureInstance): (apply: ConcurrentFutureInstance RB>) => ConcurrentFutureInstance /** Apply the function in the right Future to the value in the left Future in parallel. See https://github.com/fluture-js/Fluture#pap */ export function pap(value: FutureInstance): (apply: FutureInstance RB>) => FutureInstance /** Create a Future which resolves with the return value of the given function, or rejects with the error it throws. See https://github.com/fluture-js/Fluture#attempt */ export function attempt(fn: () => R): FutureInstance /** Convert a Promise-returning function to a Future. See https://github.com/fluture-js/Fluture#attemptP */ export function attemptP(fn: () => Promise): FutureInstance /** Create a Future using the inner value of the given Future. See https://github.com/fluture-js/Fluture#bichain */ export function bichain(lmapper: (reason: LA) => FutureInstance): (rmapper: (value: RA) => FutureInstance) => (source: FutureInstance) => FutureInstance /** Map over both branches of the given Bifunctor at once. See https://github.com/fluture-js/Fluture#bimap */ export function bimap(lmapper: (reason: LA) => LB): (rmapper: (value: RA) => RB) => (source: FutureInstance) => FutureInstance /** Wait for both Futures to resolve in parallel. See https://github.com/fluture-js/Fluture#both */ export function both(left: FutureInstance): (right: FutureInstance) => FutureInstance /** Cache the outcome of the given Future. See https://github.com/fluture-js/Fluture#cache */ export function cache(source: FutureInstance): FutureInstance /** Create a Future using the resolution value of the given Future. See https://github.com/fluture-js/Fluture#chain */ export function chain(mapper: (value: RA) => FutureInstance): (source: FutureInstance) => FutureInstance /** Create a Future using the rejection reason of the given Future. See https://github.com/fluture-js/Fluture#chain */ export function chainRej(mapper: (reason: LA) => FutureInstance): (source: FutureInstance) => FutureInstance /** Fork the given Future into a Node-style callback. See https://github.com/fluture-js/Fluture#done */ export function done(callback: Nodeback): (source: FutureInstance) => Cancel /** Encase the given function such that it returns a Future of its return value. See https://github.com/fluture-js/Fluture#encase */ export function encase(fn: (a: A) => R): (a: A) => FutureInstance /** Encase the given Promise-returning function such that it returns a Future of its resolution value. See https://github.com/fluture-js/Fluture#encasep */ export function encaseP(fn: (a: A) => Promise): (a: A) => FutureInstance /** Attempt to extract the rejection reason. See https://github.com/fluture-js/Fluture#extractleft */ export function extractLeft(source: FutureInstance): Array /** Attempt to extract the resolution value. See https://github.com/fluture-js/Fluture#extractright */ export function extractRight(source: FutureInstance): Array /** Coalesce both branches into the resolution branch. See https://github.com/fluture-js/Fluture#coalesce */ export function coalesce(lmapper: (left: LA) => R): (rmapper: (right: RA) => R) => (source: FutureInstance) => FutureInstance /** Fork the given Future into the given continuations. See https://github.com/fluture-js/Fluture#fork */ export function fork(reject: RejectFunction): (resolve: ResolveFunction) => (source: FutureInstance) => Cancel /** Fork with exception recovery. See https://github.com/fluture-js/Fluture#forkCatch */ export function forkCatch(recover: RecoverFunction): (reject: RejectFunction) => (resolve: ResolveFunction) => (source: FutureInstance) => Cancel /** Build a coroutine using Futures. See https://github.com/fluture-js/Fluture#go */ export function go(generator: () => Generator, R, any>): FutureInstance /** Manage resources before and after the computation that needs them. See https://github.com/fluture-js/Fluture#hook */ export function hook(acquire: FutureInstance): (dispose: (handle: H) => FutureInstance) => (consume: (handle: H) => FutureInstance) => FutureInstance /** Returns true for Futures. See https://github.com/fluture-js/Fluture#isfuture */ export function isFuture(value: any): boolean /** Returns true for Futures that will certainly never settle. See https://github.com/fluture-js/Fluture#isnever */ export function isNever(value: any): boolean /** Set up a cleanup Future to run after the given action has settled. See https://github.com/fluture-js/Fluture#lastly */ export function lastly(cleanup: FutureInstance): (action: FutureInstance) => FutureInstance /** Map over the resolution value of the given Future or ConcurrentFuture. See https://github.com/fluture-js/Fluture#map */ export const map: { >(mapper: Functor extends F ? never : (a: F extends Functor ? A : never) => B): (source: F) => Mapped (mapper: (a: A) => B): >(source: F) => Mapped } /** Map over the rejection reason of the given Future. See https://github.com/fluture-js/Fluture#maprej */ export function mapRej(mapper: (reason: LA) => LB): (source: FutureInstance) => FutureInstance /** A Future that never settles. See https://github.com/fluture-js/Fluture#never */ export var never: FutureInstance /** Create a Future using a provided Node-style callback. See https://github.com/fluture-js/Fluture#node */ export function node(fn: (done: Nodeback) => void): FutureInstance /** Create a Future with the given resolution value. See https://github.com/fluture-js/Fluture#of */ export function resolve(value: R): FutureInstance /** Run an Array of Futures in parallel, under the given concurrency limit. See https://github.com/fluture-js/Fluture#parallel */ export function parallel(concurrency: number): (futures: Array>) => FutureInstance> /** Convert a Future to a Promise. See https://github.com/fluture-js/Fluture#promise */ export function promise(source: FutureInstance): Promise /** Race two Futures against one another. See https://github.com/fluture-js/Fluture#race */ export function race(left: FutureInstance): (right: FutureInstance) => FutureInstance /** Create a Future with the given rejection reason. See https://github.com/fluture-js/Fluture#reject */ export function reject(reason: L): FutureInstance /** Creates a Future which rejects after the given duration with the given reason. See https://github.com/fluture-js/Fluture#rejectafter */ export function rejectAfter(duration: number): (reason: L) => FutureInstance /** Convert a ConcurrentFuture to a regular Future. See https://github.com/fluture-js/Fluture#concurrentfuture */ export function seq(source: ConcurrentFutureInstance): FutureInstance /** Swap the rejection reason and the resolution value. See https://github.com/fluture-js/Fluture#swap */ export function swap(source: FutureInstance): FutureInstance /** Fork the Future into the given continuation. See https://github.com/fluture-js/Fluture#value */ export function value(resolve: ResolveFunction): (source: FutureInstance) => Cancel /** Enable or disable debug mode. See https://github.com/fluture-js/Fluture#debugmode */ export function debugMode(debug: boolean): void; export interface FutureTypeRep { /** Create a Future from a possibly cancellable computation. See https://github.com/fluture-js/Fluture#future */ (computation: ( reject: RejectFunction, resolve: ResolveFunction ) => Cancel): FutureInstance 'fantasy-land/chainRec'(iterator: (next: (value: I) => IteratorYieldResult, done: (value: R) => IteratorReturnResult, value: I) => FutureInstance>, initial: I): FutureInstance 'fantasy-land/of': typeof resolve '@@type': string } export var Future: FutureTypeRep export default Future export interface ConcurrentFutureTypeRep { /** Create a ConcurrentFuture using a Future. See https://github.com/fluture-js/Fluture#concurrentfuture */ (source: FutureInstance): ConcurrentFutureInstance 'fantasy-land/of'(value: R): ConcurrentFutureInstance 'fantasy-land/zero'(): ConcurrentFutureInstance '@@type': string } export var Par: ConcurrentFutureTypeRep ================================================ FILE: index.js ================================================ export { Future as default, Future, isFuture, isNever, never, reject, resolve, } from './src/future.js'; export {after} from './src/after.js'; export {alt} from './src/alt.js'; export {and} from './src/and.js'; export {ap} from './src/ap.js'; export {attemptP} from './src/attempt-p.js'; export {attempt} from './src/attempt.js'; export {bimap} from './src/bimap.js'; export {bichain} from './src/bichain.js'; export {both} from './src/both.js'; export {cache} from './src/cache.js'; export {chainRej} from './src/chain-rej.js'; export {chain} from './src/chain.js'; export {done} from './src/done.js'; export {encaseP} from './src/encase-p.js'; export {encase} from './src/encase.js'; export {extractLeft} from './src/extract-left.js'; export {extractRight} from './src/extract-right.js'; export {coalesce} from './src/coalesce.js'; export {forkCatch} from './src/fork-catch.js'; export {fork} from './src/fork.js'; export {go} from './src/go.js'; export {hook} from './src/hook.js'; export {lastly} from './src/lastly.js'; export {mapRej} from './src/map-rej.js'; export {map} from './src/map.js'; export {node} from './src/node.js'; export {pap} from './src/pap.js'; export {parallel} from './src/parallel.js'; export {Par} from './src/par.js'; export {promise} from './src/promise.js'; export {race} from './src/race.js'; export {rejectAfter} from './src/reject-after.js'; export {seq} from './src/seq.js'; export {swap} from './src/swap.js'; export {value} from './src/value.js'; export {debugMode} from './src/internal/debug.js'; ================================================ FILE: package.json ================================================ { "name": "fluture", "version": "14.0.0", "description": "FantasyLand compliant (monadic) alternative to Promises", "main": "index.cjs", "type": "module", "types": "index.d.ts", "module": "index.js", "exports": { ".": { "require": "./index.cjs", "import": "./index.js", "default": "./index.cjs" }, "./index.js": "./index.js", "./test/arbitraries.js": "./test/arbitraries.js", "./test/assertions.js": "./test/assertions.js" }, "files": [ "src", "index.cjs", "index.js", "index.d.ts", "test/arbitraries.js", "test/assertions.js" ], "repository": "https://github.com/fluture-js/Fluture.git", "scripts": { "bench": "node ./scripts/bench.js", "build": "rollup -c rollup.config.js", "build:dist": "rollup -c rollup.config.dist.js", "clean": "rimraf npm-debug.log coverage index.cjs .esm-cache .nyc_output node_modules/.cache", "lint": "eslint src test index.js index.cjs.js", "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 && ./scripts/test-esm && npm run test:code && npm run test:types && npm run test:build", "test:code": "c8 oletus test/unit/*.js test/integration/*.js test/prop/*.js", "test:mem": "NODE_OPTIONS='--experimental-modules --no-warnings' ./scripts/test-mem", "test:build": "npm run build && oletus test/build/*.js", "coverage:upload": "c8 report --reporter=text-lcov > coverage.lcov && codecov", "coverage:report": "c8 report --reporter=html", "test:types": "tsd" }, "author": "Aldwin Vlasblom (https://github.com/Avaq)", "homepage": "https://github.com/fluture-js/Fluture", "bugs": { "url": "https://github.com/fluture-js/Fluture/issues" }, "license": "MIT", "engines": { "node": ">=4.0.0" }, "keywords": [ "algebraic", "async", "asynchronous", "browser", "control-flow", "fantasy-land", "fp", "functional", "functor", "future", "library", "monad", "monadic", "node", "parallel", "promise", "sequential" ], "dependencies": { "sanctuary-show": "^2.0.0", "sanctuary-type-identifiers": "^4.0.0" }, "devDependencies": { "@rollup/plugin-commonjs": "^25.0.0", "@rollup/plugin-node-resolve": "^15.0.1", "c8": "^9.0.0", "chai": "^4.1.2", "codecov": "^3.6.1", "es-check": "^7.0.0", "eslint": "^8.54.0", "eslint-config-warp": "^7.1.0", "fantasy-laws": "^2.0.1", "jsverify": "^0.8.3", "oletus": "^4.0.0", "ramda": "^0.29.1", "remark-cli": "^12.0.0", "remark-validate-links": "^13.0.0", "rimraf": "^5.0.0", "rollup": "^4.5.2", "sanctuary-benchmark": "^1.0.0", "sanctuary-either": "^2.0.0", "sanctuary-type-classes": "^13.0.0", "tsd": "^0.30.0", "typescript": "^5.0.4", "xyz": "^4.0.0" }, "tsd": { "directory": "test/types" } } ================================================ FILE: rollup.config.dist.js ================================================ /* global process Set */ import {readFileSync} from 'fs'; import node from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; var pkg = JSON.parse(readFileSync('package.json', 'utf8')); var dependencies = pkg => { var deps = Object.keys(pkg.dependencies || {}).concat(Object.keys(pkg.peerDependencies || {})); return Array.from(new Set(deps.concat(deps.flatMap(dependency => ( dependencies(JSON.parse(readFileSync(`node_modules/${dependency}/package.json`, 'utf8'))) ))))); }; var banner = `/** * Fluture bundled; version ${process.env.VERSION || `${pkg.version} (dirty)`} */ `; var footer = `/** Fluture license ${readFileSync('./LICENSE')}*/ ${dependencies(pkg).map(dependency => `/** ${dependency} license ${readFileSync(`./node_modules/${dependency}/LICENSE`)}*/`).join('\n\n')}`; var typeref = `/// `; export default [{ input: 'index.cjs.js', plugins: [node(), commonjs({include: 'node_modules/**'})], output: { banner: banner, footer: footer, format: 'iife', name: 'Fluture', file: 'dist/bundle.js', }, }, { input: 'index.js', plugins: [node(), commonjs({include: 'node_modules/**'})], output: { banner: `${banner}\n${typeref}\n`, footer: footer, format: 'es', file: 'dist/module.js', }, }]; ================================================ FILE: rollup.config.js ================================================ var dependencies = { 'sanctuary-show': 'sanctuaryShow', 'sanctuary-type-identifiers': 'sanctuaryTypeIdentifiers', }; export default { input: 'index.cjs.js', external: Object.keys(dependencies), output: { format: 'umd', file: 'index.cjs', name: 'Fluture', globals: dependencies, }, }; ================================================ FILE: scripts/bench.js ================================================ const suite = process.argv[2]; const options = process.argv[3] ? JSON.parse(process.argv[3]) : {}; import(`../bench/${suite}.js`).then(module => ( module.default(options) ), e => { console.error(e.stack); }); ================================================ FILE: scripts/distribute ================================================ #!/usr/bin/env bash set -eufo pipefail echo "Publishing as $(npm whoami)." if ! npm outdated --long; then read -rp "Continue? [y/N] " choice if [[ "${choice-n}" != 'y' ]]; then echo 'Package distribution aborted.' exit 2 fi fi npm run build npm run build:dist sed --in-place "s/Fluture@$PREVIOUS_VERSION/Fluture@$VERSION/" README.md git add dist README.md ================================================ FILE: scripts/test-esm ================================================ #!/usr/bin/env bash set -eufo pipefail version=$(node --print 'process.versions.node.split(".")[0]') if [[ "$version" -lt 12 ]]; then echo 'Skipping esm test on Node versions below 12' elif node --experimental-modules --no-warnings index.js; then echo 'No problems with the EcmaScript module' else echo 'Problem encountered loading the EcmaScript module' exit 1 fi ================================================ FILE: scripts/test-mem ================================================ #!/usr/bin/env node /* global process setImmediate */ import {Future, fork, race, chain, resolve as sync} from '../index.js'; import {log} from 'util'; var start = Date.now(); var batch = 0; var stamp = Date.now(); function report(){ var memMB = process.memoryUsage().rss / 1048576; var now = Date.now(); var passed = now - stamp; batch = batch + 1; if(passed >= 5000){ log( '-BATCH:', batch, '-OPS:', Math.round(batch / ((now - start) / passed) / (passed / 1000)), '-MEM:', memMB, 'MB' ); stamp = now; } } function noop(){} function async(x){ return Future(function(l, r){ setImmediate(r, x); return noop }); } var cases = Object.create(null); //Should infinitely run until finally running out of memory. cases.syncHeadRecursion = function recur(){ report(); return race(sync('r'))(chain(recur)(sync('l'))); }; //Should immediately exit with "l". cases.syncDeepRecursion = function recur(){ report(); return race(chain(recur)(sync('r')))(sync('l')); }; //Should infinitely run without any problems. cases.syncTailRecursion = function recur(){ report(); return chain(recur)(race(sync('r'))(sync('l'))); }; //Should immediately exit with "r". cases.asyncHeadRecursion = function recur(){ report(); return race(async('r'))(chain(recur)(async('l'))); }; //Should immediately exit with "l". cases.asyncDeepRecursion = function recur(){ report(); return race(chain(recur)(async('r')))(async('l')); }; //Should infinitely run without any problems. cases.asyncTailRecursion = function recur(){ report(); return chain(recur)(race(async('r'))(async('l'))); }; //Expected to run out of memory. cases.debugModeSyncTailRecursion = function(){ Future.debugMode(true); return cases.syncTailRecursion(); }; //Expected to run out of memory. cases.debugModeAsyncTailRecursion = function(){ Future.debugMode(true); return cases.asyncTailRecursion(); }; var f = cases[process.argv[2]]; if(typeof f !== 'function'){ console.log('Usage:\n\n test-mem \n\nPossible cases:\n'); Object.keys(cases).forEach(function(k){console.log(` ${k}`)}); process.exit(0); } log('PID', process.pid); var cancel = fork (function(e){console.error(e.stack); process.exit(1)}) (function(v){log('resolved', v)}) (f()); process.once('SIGINT', () => { log('SIGINT caught. Cancelling...'); cancel(); }); ================================================ FILE: src/after.js ================================================ import { any, application, application1, createInterpreter, never, positiveInteger, } from './future.js'; export var After = createInterpreter(2, 'after', function After$interpret(rec, rej, res){ var id = setTimeout(res, this.$1, this.$2); return function After$cancel(){ clearTimeout(id) }; }); After.prototype.extractRight = function After$extractRight(){ return [this.$2]; }; function alwaysNever(_){ return never; } export function after(time){ var context1 = application1(after, positiveInteger, arguments); return time === Infinity ? alwaysNever : (function after(value){ var context2 = application(2, after, any, arguments, context1); return new After(context2, time, value); }); } ================================================ FILE: src/alt.js ================================================ import {FL} from './internal/const.js'; import {invalidArgumentOf} from './internal/error.js'; import {isAlt} from './internal/predicates.js'; import { AltTransformation, application, application1, future, isFuture, } from './future.js'; export var alternative = {pred: isAlt, error: invalidArgumentOf('have Alt implemented')}; export function alt(left){ if(isFuture(left)){ var context1 = application1(alt, future, arguments); return function alt(right){ var context2 = application(2, alt, future, arguments, context1); return right._transform(new AltTransformation(context2, left)); }; } var context = application1(alt, alternative, arguments); return function alt(right){ application(2, alt, alternative, arguments, context); return left[FL.alt](right); }; } ================================================ FILE: src/and.js ================================================ import {createTransformation, application1, application, future} from './future.js'; export var AndTransformation = createTransformation(1, 'and', { resolved: function AndTransformation$resolved(){ return this.$1 }, }); export function and(left){ var context1 = application1(and, future, arguments); return function and(right){ var context2 = application(2, and, future, arguments, context1); return right._transform(new AndTransformation(context2, left)); }; } ================================================ FILE: src/ap.js ================================================ import {FL} from './internal/const.js'; import {invalidArgumentOf} from './internal/error.js'; import {isApply} from './internal/predicates.js'; import {isFuture, ApTransformation, application1, application, future} from './future.js'; export var apply = {pred: isApply, error: invalidArgumentOf('have Apply implemented')}; export function ap(mx){ if(isFuture(mx)){ var context1 = application1(ap, future, arguments); return function ap(mf){ var context2 = application(2, ap, future, arguments, context1); return mf._transform(new ApTransformation(context2, mx)); }; } var context = application1(ap, apply, arguments); return function ap(mf){ application(2, ap, apply, arguments, context); return mx[FL.ap](mf); }; } ================================================ FILE: src/attempt-p.js ================================================ import {encaseP} from './encase-p.js'; export function attemptP(_){ return encaseP.apply(this, arguments)(undefined); } ================================================ FILE: src/attempt.js ================================================ import {encase} from './encase.js'; export function attempt(_){ return encase.apply(this, arguments)(undefined); } ================================================ FILE: src/bichain.js ================================================ import {createTransformation, future, application1, application, func} from './future.js'; import {call} from './internal/utils.js'; export var BichainTransformation = createTransformation(2, 'bichain', { rejected: function BichainTransformation$rejected(x){ return call(this.$1, x) }, resolved: function BichainTransformation$resolved(x){ return call(this.$2, x) }, }); export function bichain(f){ var context1 = application1(bichain, func, arguments); return function bichain(g){ var context2 = application(2, bichain, func, arguments, context1); return function bichain(m){ var context3 = application(3, bichain, future, arguments, context2); return m._transform(new BichainTransformation(context3, f, g)); }; }; } ================================================ FILE: src/bimap.js ================================================ import {FL} from './internal/const.js'; import {invalidArgumentOf} from './internal/error.js'; import {isBifunctor} from './internal/predicates.js'; import {isFuture, BimapTransformation, application1, application, func} from './future.js'; export var bifunctor = {pred: isBifunctor, error: invalidArgumentOf('have Bifunctor implemented')}; export function bimap(f){ var context1 = application1(bimap, func, arguments); return function bimap(g){ var context2 = application(2, bimap, func, arguments, context1); return function bimap(m){ var context3 = application(3, bimap, bifunctor, arguments, context2); return isFuture(m) ? m._transform(new BimapTransformation(context3, f, g)) : m[FL.bimap](f, g); }; }; } ================================================ FILE: src/both.js ================================================ import {createParallelTransformation, earlyCrash, earlyReject} from './internal/parallel.js'; import {noop} from './internal/utils.js'; import {createTransformation, Resolve, application1, application, future} from './future.js'; export var PairTransformation = createTransformation(1, 'pair', { resolved: function PairTransformation$resolved(x){ return new Resolve(this.context, [x, this.$1]); }, }); export var BothTransformation = createParallelTransformation('both', earlyCrash, earlyReject, noop, { resolved: function BothTransformation$resolved(x){ return this.$1._transform(new PairTransformation(this.context, x)); }, }); export function both(left){ var context1 = application1(both, future, arguments); return function both(right){ var context2 = application(2, both, future, arguments, context1); return right._transform(new BothTransformation(context2, left)); }; } ================================================ FILE: src/cache.js ================================================ import {noop} from './internal/utils.js'; import {createInterpreter, application1, future} from './future.js'; export var Cold = 0; export var Pending = 1; export var Crashed = 2; export var Rejected = 3; export var Resolved = 4; export function Queued(rec, rej, res){ this[Crashed] = rec; this[Rejected] = rej; this[Resolved] = res; } export var Cache = createInterpreter(1, 'cache', function Cache$interpret(rec, rej, res){ var cancel = noop; switch(this._state){ /* c8 ignore next 4 */ case Pending: cancel = this._addToQueue(rec, rej, res); break; case Crashed: rec(this._value); break; case Rejected: rej(this._value); break; case Resolved: res(this._value); break; default: this._queue = []; cancel = this._addToQueue(rec, rej, res); this.run(); } return cancel; }); Cache.prototype._cancel = noop; Cache.prototype._queue = null; Cache.prototype._queued = 0; Cache.prototype._value = undefined; Cache.prototype._state = Cold; Cache.prototype.extractLeft = function Cache$extractLeft(){ return this._state === Rejected ? [this._value] : []; }; Cache.prototype.extractRight = function Cache$extractRight(){ return this._state === Resolved ? [this._value] : []; }; Cache.prototype._addToQueue = function Cache$addToQueue(rec, rej, res){ var _this = this; if(_this._state > Pending) return noop; var i = _this._queue.push(new Queued(rec, rej, res)) - 1; _this._queued = _this._queued + 1; return function Cache$removeFromQueue(){ if(_this._state > Pending) return; _this._queue[i] = undefined; _this._queued = _this._queued - 1; if(_this._queued === 0) _this.reset(); }; }; Cache.prototype._drainQueue = function Cache$drainQueue(){ if(this._state <= Pending) return; if(this._queued === 0) return; var queue = this._queue; var length = queue.length; var state = this._state; var value = this._value; for(var i = 0; i < length; i++){ queue[i] && queue[i][state](value); queue[i] = undefined; } this._queue = undefined; this._queued = 0; }; Cache.prototype.crash = function Cache$crash(error){ if(this._state > Pending) return; this._value = error; this._state = Crashed; this._drainQueue(); }; Cache.prototype.reject = function Cache$reject(reason){ if(this._state > Pending) return; this._value = reason; this._state = Rejected; this._drainQueue(); }; Cache.prototype.resolve = function Cache$resolve(value){ if(this._state > Pending) return; this._value = value; this._state = Resolved; this._drainQueue(); }; Cache.prototype.run = function Cache$run(){ var _this = this; if(_this._state > Cold) return; _this._state = Pending; _this._cancel = _this.$1._interpret( function Cache$fork$rec(x){ _this.crash(x) }, function Cache$fork$rej(x){ _this.reject(x) }, function Cache$fork$res(x){ _this.resolve(x) } ); }; Cache.prototype.reset = function Cache$reset(){ if(this._state === Cold) return; if(this._state === Pending) this._cancel(); this._cancel = noop; this._queue = []; this._queued = 0; this._value = undefined; this._state = Cold; }; export function cache(m){ return new Cache(application1(cache, future, arguments), m); } ================================================ FILE: src/chain-rej.js ================================================ import {call} from './internal/utils.js'; import {createTransformation, application1, application, future, func} from './future.js'; export var ChainRejTransformation = createTransformation(1, 'chainRej', { rejected: function ChainRejTransformation$rejected(x){ return call(this.$1, x) }, }); export function chainRej(f){ var context1 = application1(chainRej, func, arguments); return function chainRej(m){ var context2 = application(2, chainRej, future, arguments, context1); return m._transform(new ChainRejTransformation(context2, f)); }; } ================================================ FILE: src/chain.js ================================================ import {FL} from './internal/const.js'; import {invalidArgumentOf} from './internal/error.js'; import {isChain} from './internal/predicates.js'; import {isFuture, application1, application, func, ChainTransformation} from './future.js'; export var monad = {pred: isChain, error: invalidArgumentOf('have Chain implemented')}; export function chain(f){ var context1 = application1(chain, func, arguments); return function chain(m){ var context2 = application(2, chain, monad, arguments, context1); return isFuture(m) ? m._transform(new ChainTransformation(context2, f)) : m[FL.chain](f); }; } ================================================ FILE: src/coalesce.js ================================================ import {call} from './internal/utils.js'; import {createTransformation, Resolve, application1, application, func, future} from './future.js'; export var CoalesceTransformation = createTransformation(2, 'coalesce', { rejected: function CoalesceTransformation$rejected(x){ return new Resolve(this.context, call(this.$1, x)); }, resolved: function CoalesceTransformation$resolved(x){ return new Resolve(this.context, call(this.$2, x)); }, }); export function coalesce(f){ var context1 = application1(coalesce, func, arguments); return function coalesce(g){ var context2 = application(2, coalesce, func, arguments, context1); return function coalesce(m){ var context3 = application(3, coalesce, future, arguments, context2); return m._transform(new CoalesceTransformation(context3, f, g)); }; }; } ================================================ FILE: src/done.js ================================================ import {application1, application, func, future} from './future.js'; import {raise} from './internal/utils.js'; export function done(callback){ var context1 = application1(done, func, arguments); function done$res(x){ callback(null, x); } return function done(m){ application(2, done, future, arguments, context1); return m._interpret(raise, callback, done$res); }; } ================================================ FILE: src/encase-p.js ================================================ import {wrapException, typeError} from './internal/error.js'; import {isThenable} from './internal/predicates.js'; import {noop, show} from './internal/utils.js'; import {createInterpreter, application1, application, func, any} from './future.js'; function invalidPromise(p, f, a){ return typeError( 'encaseP() expects the function it\'s given to return a Promise/Thenable' + '\n Actual: ' + show(p) + '\n From calling: ' + show(f) + '\n With: ' + show(a) ); } export var EncaseP = createInterpreter(2, 'encaseP', function EncaseP$interpret(rec, rej, res){ var open = true, fn = this.$1, arg = this.$2, p; try{ p = fn(arg); }catch(e){ rec(wrapException(e, this)); return noop; } if(!isThenable(p)){ rec(wrapException(invalidPromise(p, fn, arg), this)); return noop; } p.then(function EncaseP$res(x){ if(open){ open = false; res(x); } }, function EncaseP$rej(x){ if(open){ open = false; rej(x); } }); return function EncaseP$cancel(){ open = false }; }); export function encaseP(f){ var context1 = application1(encaseP, func, arguments); return function encaseP(x){ var context2 = application(2, encaseP, any, arguments, context1); return new EncaseP(context2, f, x); }; } ================================================ FILE: src/encase.js ================================================ import {noop} from './internal/utils.js'; import {createInterpreter, application1, application, func, any} from './future.js'; export var Encase = createInterpreter(2, 'encase', function Encase$interpret(rec, rej, res){ var fn = this.$1, r; try{ r = fn(this.$2) }catch(e){ rej(e); return noop } res(r); return noop; }); export function encase(f){ var context1 = application1(encase, func, arguments); return function encase(x){ var context2 = application(2, encase, any, arguments, context1); return new Encase(context2, f, x); }; } ================================================ FILE: src/extract-left.js ================================================ import {application1, future} from './future.js'; export function extractLeft(m){ application1(extractLeft, future, arguments); return m.extractLeft(); } ================================================ FILE: src/extract-right.js ================================================ import {application1, future} from './future.js'; export function extractRight(m){ application1(extractRight, future, arguments); return m.extractRight(); } ================================================ FILE: src/fork-catch.js ================================================ import {application, application1, func, future} from './future.js'; export function forkCatch(f){ var context1 = application1(forkCatch, func, arguments); return function forkCatch(g){ var context2 = application(2, forkCatch, func, arguments, context1); return function forkCatch(h){ var context3 = application(3, forkCatch, func, arguments, context2); return function forkCatch(m){ application(4, forkCatch, future, arguments, context3); return m._interpret(f, g, h); }; }; }; } ================================================ FILE: src/fork.js ================================================ import {raise} from './internal/utils.js'; import {application, application1, func, future} from './future.js'; export function fork(f){ var context1 = application1(fork, func, arguments); return function fork(g){ var context2 = application(2, fork, func, arguments, context1); return function fork(m){ application(3, fork, future, arguments, context2); return m._interpret(raise, f, g); }; }; } ================================================ FILE: src/future.js ================================================ /* eslint no-cond-assign:0, no-constant-condition:0 */ import type from 'sanctuary-type-identifiers'; import {FL, $$type} from './internal/const.js'; import {captureContext, captureApplicationContext, captureStackTrace} from './internal/debug.js'; import { invalidArgument, invalidArgumentOf, invalidArity, invalidFuture, invalidFutureArgument, typeError, withExtraContext, wrapException, } from './internal/error.js'; import {Next, Done} from './internal/iteration.js'; import {nil, cons, isNil, reverse, toArray} from './internal/list.js'; import {isFunction, isUnsigned} from './internal/predicates.js'; import {show, noop, call, moop} from './internal/utils.js'; function alwaysTrue(){ return true; } function getArgs(it){ var args = new Array(it.arity); for(var i = 1; i <= it.arity; i++){ args[i - 1] = it['$' + String(i)]; } return args; } function showArg(arg){ return ' (' + show(arg) + ')'; } export var any = {pred: alwaysTrue, error: invalidArgumentOf('be anything')}; export var func = {pred: isFunction, error: invalidArgumentOf('be a Function')}; export var future = {pred: isFuture, error: invalidFutureArgument}; export var positiveInteger = {pred: isUnsigned, error: invalidArgumentOf('be a positive Integer')}; export function application(n, f, type, args, prev){ if(args.length < 2 && type.pred(args[0])) return captureApplicationContext(prev, n, f); var e = args.length > 1 ? invalidArity(f, args) : type.error(f.name, n - 1, args[0]); captureStackTrace(e, f); throw withExtraContext(e, prev); } export function application1(f, type, args){ return application(1, f, type, args, nil); } export function Future(computation){ var context = application1(Future, func, arguments); return new Computation(context, computation); } export function isFuture(x){ return x instanceof Future || type(x) === $$type; } // Compliance with sanctuary-type-identifiers versions 1 and 2. // To prevent sanctuary-type-identifiers version 3 from identifying 'Future' // as being of the type denoted by $$type, we ensure that // Future.constructor.prototype is equal to Future. Future['@@type'] = $$type; Future.constructor = {prototype: Future}; Future[FL.of] = resolve; Future[FL.chainRec] = chainRec; Future.prototype['@@type'] = $$type; Future.prototype['@@show'] = function Future$show(){ return this.toString(); }; Future.prototype.pipe = function Future$pipe(f){ if(!isFunction(f)) throw invalidArgument('Future#pipe', 0, 'be a Function', f); return f(this); }; Future.prototype[FL.ap] = function Future$FL$ap(other){ var context = captureContext(nil, 'a Fantasy Land dispatch to ap', Future$FL$ap); return other._transform(new ApTransformation(context, this)); }; Future.prototype[FL.map] = function Future$FL$map(mapper){ var context = captureContext(nil, 'a Fantasy Land dispatch to map', Future$FL$map); return this._transform(new MapTransformation(context, mapper)); }; Future.prototype[FL.bimap] = function Future$FL$bimap(lmapper, rmapper){ var context = captureContext(nil, 'a Fantasy Land dispatch to bimap', Future$FL$bimap); return this._transform(new BimapTransformation(context, lmapper, rmapper)); }; Future.prototype[FL.chain] = function Future$FL$chain(mapper){ var context = captureContext(nil, 'a Fantasy Land dispatch to chain', Future$FL$chain); return this._transform(new ChainTransformation(context, mapper)); }; Future.prototype[FL.alt] = function Future$FL$alt(other){ var context = captureContext(nil, 'a Fantasy Land dispatch to alt', Future$FL$alt); return this._transform(new AltTransformation(context, other)); }; Future.prototype.extractLeft = function Future$extractLeft(){ return []; }; Future.prototype.extractRight = function Future$extractRight(){ return []; }; Future.prototype._transform = function Future$transform(transformation){ return new Transformer(transformation.context, this, cons(transformation, nil)); }; Future.prototype.isTransformer = false; Future.prototype.context = nil; Future.prototype.arity = 0; Future.prototype.name = 'future'; Future.prototype.toString = function Future$toString(){ return this.name + getArgs(this).map(showArg).join(''); }; Future.prototype.toJSON = function Future$toJSON(){ return {$: $$type, kind: 'interpreter', type: this.name, args: getArgs(this)}; }; export function createInterpreter(arity, name, interpret){ var Interpreter = function(context, $1, $2, $3){ this.context = context; this.$1 = $1; this.$2 = $2; this.$3 = $3; }; Interpreter.prototype = Object.create(Future.prototype); Interpreter.prototype.arity = arity; Interpreter.prototype.name = name; Interpreter.prototype._interpret = interpret; return Interpreter; } export var Computation = createInterpreter(1, 'Future', function Computation$interpret(rec, rej, res){ var computation = this.$1, open = false, cancel = noop, cont = function(){ open = true }; try{ cancel = computation(function Computation$rej(x){ cont = function Computation$rej$cont(){ open = false; rej(x); }; if(open){ cont(); } }, function Computation$res(x){ cont = function Computation$res$cont(){ open = false; res(x); }; if(open){ cont(); } }); }catch(e){ rec(wrapException(e, this)); return noop; } if(!(isFunction(cancel) && cancel.length === 0)){ rec(wrapException(typeError( 'The computation was expected to return a nullary cancellation function\n' + ' Actual: ' + show(cancel) ), this)); return noop; } cont(); return function Computation$cancel(){ if(open){ open = false; cancel && cancel(); } }; }); export var Never = createInterpreter(0, 'never', function Never$interpret(){ return noop; }); Never.prototype._isNever = true; export var never = new Never(nil); export function isNever(x){ return isFuture(x) && x._isNever === true; } export var Crash = createInterpreter(1, 'crash', function Crash$interpret(rec){ rec(this.$1); return noop; }); export function crash(x){ return new Crash(application1(crash, any, arguments), x); } export var Reject = createInterpreter(1, 'reject', function Reject$interpret(rec, rej){ rej(this.$1); return noop; }); Reject.prototype.extractLeft = function Reject$extractLeft(){ return [this.$1]; }; export function reject(x){ return new Reject(application1(reject, any, arguments), x); } export var Resolve = createInterpreter(1, 'resolve', function Resolve$interpret(rec, rej, res){ res(this.$1); return noop; }); Resolve.prototype.extractRight = function Resolve$extractRight(){ return [this.$1]; }; export function resolve(x){ return new Resolve(application1(resolve, any, arguments), x); } // Note: This function is not curried because it's only used to satisfy the // Fantasy Land ChainRec specification. export function chainRec(step, init){ return resolve(Next(init))._transform(new ChainTransformation(nil, function chainRec$recur(o){ return o.done ? resolve(o.value) : step(Next, Done, o.value)._transform(new ChainTransformation(nil, chainRec$recur)); })); } export var Transformer = createInterpreter(2, 'transform', function Transformer$interpret(rec, rej, res){ // These are the cold, and hot, transformation stacks. The cold actions are those that // have yet to run parallel computations, and hot are those that have. var cold = nil, hot = nil; // These combined variables define our current state. // future = the future we are currently forking // transformation = the transformation to be informed when the future settles // cancel = the cancel function of the current future // settled = a boolean indicating whether a new tick should start // async = a boolean indicating whether we are awaiting a result asynchronously var future, transformation, cancel = noop, settled, async = true, it; // Takes a transformation from the top of the hot stack and returns it. function nextHot(){ var x = hot.head; hot = hot.tail; return x; } // Takes a transformation from the top of the cold stack and returns it. function nextCold(){ var x = cold.head; cold = cold.tail; return x; } // This function is called with a future to use in the next tick. // Here we "flatten" the actions of another Sequence into our own actions, // this is the magic that allows for infinitely stack safe recursion because // actions like ChainAction will return a new Sequence. // If we settled asynchronously, we call drain() directly to run the next tick. function settle(m){ settled = true; future = m; if(future.isTransformer){ var tail = future.$2; while(!isNil(tail)){ cold = cons(tail.head, cold); tail = tail.tail; } future = future.$1; } if(async) drain(); } // This function serves as a rejection handler for our current future. // It will tell the current transformation that the future rejected, and it will // settle the current tick with the transformation's answer to that. function rejected(x){ settle(transformation.rejected(x)); } // This function serves as a resolution handler for our current future. // It will tell the current transformation that the future resolved, and it will // settle the current tick with the transformation's answer to that. function resolved(x){ settle(transformation.resolved(x)); } // This function is passed into actions when they are "warmed up". // If the transformation decides that it has its result, without the need to await // anything else, then it can call this function to force "early termination". // When early termination occurs, all actions which were stacked prior to the // terminator will be skipped. If they were already hot, they will also be // sent a cancel signal so they can cancel their own concurrent computations, // as their results are no longer needed. function early(m, terminator){ cancel(); cold = nil; if(async && transformation !== terminator){ transformation.cancel(); while((it = nextHot()) && it !== terminator) it.cancel(); } settle(m); } // This will cancel the current Future, the current transformation, and all stacked hot actions. function Sequence$cancel(){ cancel(); transformation && transformation.cancel(); while(it = nextHot()) it.cancel(); } // This function is called when an exception is caught. function exception(e){ Sequence$cancel(); settled = true; cold = hot = nil; var error = wrapException(e, future); future = never; rec(error); } // This function serves to kickstart concurrent computations. // Takes all actions from the cold stack in reverse order, and calls run() on // each of them, passing them the "early" function. If any of them settles (by // calling early()), we abort. After warming up all actions in the cold queue, // we warm up the current transformation as well. function warmupActions(){ cold = reverse(cold); while(cold !== nil){ it = cold.head.run(early); if(settled) return; hot = cons(it, hot); cold = cold.tail; } transformation = transformation.run(early); } // This function represents our main execution loop. By "tick", we've been // referring to the execution of one iteration in the while-loop below. function drain(){ async = false; while(true){ settled = false; if(transformation = nextCold()){ cancel = future._interpret(exception, rejected, resolved); if(!settled) warmupActions(); }else if(transformation = nextHot()){ cancel = future._interpret(exception, rejected, resolved); }else break; if(settled) continue; async = true; return; } cancel = future._interpret(exception, rej, res); } // Start the execution loop. settle(this); // Return the cancellation function. return Sequence$cancel; }); Transformer.prototype.isTransformer = true; Transformer.prototype._transform = function Transformer$_transform(transformation){ return new Transformer(transformation.context, this.$1, cons(transformation, this.$2)); }; Transformer.prototype.toString = function Transformer$toString(){ return toArray(reverse(this.$2)).reduce(function(str, action){ return action.name + getArgs(action).map(showArg).join('') + ' (' + str + ')'; }, this.$1.toString()); }; function BaseTransformation$rejected(x){ this.cancel(); return new Reject(this.context, x); } function BaseTransformation$resolved(x){ this.cancel(); return new Resolve(this.context, x); } function BaseTransformation$toJSON(){ return {$: $$type, kind: 'transformation', type: this.name, args: getArgs(this)}; } export var BaseTransformation = { rejected: BaseTransformation$rejected, resolved: BaseTransformation$resolved, run: moop, cancel: noop, context: nil, arity: 0, name: 'transform', toJSON: BaseTransformation$toJSON, }; function wrapHandler(handler){ return function transformationHandler(x){ var m; try{ m = handler.call(this, x); }catch(e){ return new Crash(this.context, e); } if(isFuture(m)){ return m; } return new Crash(this.context, invalidFuture( this.name + ' expects the return value from the function it\'s given', m, '\n When called with: ' + show(x) )); }; } export function createTransformation(arity, name, prototype){ var Transformation = function(context, $1, $2){ this.context = context; this.$1 = $1; this.$2 = $2; }; Transformation.prototype = Object.create(BaseTransformation); Transformation.prototype.arity = arity; Transformation.prototype.name = name; if(typeof prototype.rejected === 'function'){ Transformation.prototype.rejected = wrapHandler(prototype.rejected); } if(typeof prototype.resolved === 'function'){ Transformation.prototype.resolved = wrapHandler(prototype.resolved); } if(typeof prototype.run === 'function'){ Transformation.prototype.run = prototype.run; } return Transformation; } export var ApTransformation = createTransformation(1, 'ap', { resolved: function ApTransformation$resolved(f){ if(isFunction(f)) return this.$1._transform(new MapTransformation(this.context, f)); throw typeError( 'ap expects the second Future to resolve to a Function\n' + ' Actual: ' + show(f) ); }, }); export var AltTransformation = createTransformation(1, 'alt', { rejected: function AltTransformation$rejected(){ return this.$1 }, }); export var MapTransformation = createTransformation(1, 'map', { resolved: function MapTransformation$resolved(x){ return new Resolve(this.context, call(this.$1, x)); }, }); export var BimapTransformation = createTransformation(2, 'bimap', { rejected: function BimapTransformation$rejected(x){ return new Reject(this.context, call(this.$1, x)); }, resolved: function BimapTransformation$resolved(x){ return new Resolve(this.context, call(this.$2, x)); }, }); export var ChainTransformation = createTransformation(1, 'chain', { resolved: function ChainTransformation$resolved(x){ return call(this.$1, x) }, }); ================================================ FILE: src/go.js ================================================ /* eslint consistent-return: 0 */ import {typeError, invalidFuture, invalidArgument, wrapException} from './internal/error.js'; import {isIteration} from './internal/iteration.js'; import {isIterator} from './internal/predicates.js'; import {Undetermined, Synchronous, Asynchronous} from './internal/timing.js'; import {show, noop} from './internal/utils.js'; import {createInterpreter, isFuture, application1, func} from './future.js'; export function invalidIteration(o){ return typeError( 'The iterator did not return a valid iteration from iterator.next()\n' + ' Actual: ' + show(o) ); } export function invalidState(x){ return invalidFuture( 'go() expects the value produced by the iterator', x, '\n Tip: If you\'re using a generator, make sure you always yield a Future' ); } export var Go = createInterpreter(1, 'go', function Go$interpret(rec, rej, res){ var _this = this, timing = Undetermined, cancel = noop, state, value, iterator; function crash(e){ rec(wrapException(e, _this)); } try{ iterator = _this.$1(); }catch(e){ crash(e); return noop; } if(!isIterator(iterator)){ crash(invalidArgument('go', 0, 'return an iterator, maybe you forgot the "*"', iterator)); return noop; } function resolved(x){ value = x; if(timing === Asynchronous) return drain(); timing = Synchronous; } function drain(){ // eslint-disable-next-line no-constant-condition while(true){ try{ state = iterator.next(value); }catch(e){ return crash(e); } if(!isIteration(state)) return crash(invalidIteration(state)); if(state.done) break; if(!isFuture(state.value)) return crash(invalidState(state.value)); timing = Undetermined; cancel = state.value._interpret(crash, rej, resolved); if(timing === Undetermined) return (timing = Asynchronous); } res(state.value); } drain(); return function Go$cancel(){ cancel() }; }); export function go(generator){ return new Go(application1(go, func, arguments), generator); } ================================================ FILE: src/hook.js ================================================ import {noop, show, raise} from './internal/utils.js'; import {invalidFuture, wrapException} from './internal/error.js'; import {createInterpreter, isFuture, application1, application, func, future} from './future.js'; function invalidDisposal(m, f, x){ return invalidFuture( 'hook() expects the return value from the first function it\'s given', m, '\n From calling: ' + show(f) + '\n With: ' + show(x) ); } function invalidConsumption(m, f, x){ return invalidFuture( 'hook() expects the return value from the second function it\'s given', m, '\n From calling: ' + show(f) + '\n With: ' + show(x) ); } export var Hook = createInterpreter(3, 'hook', function Hook$interpret(rec, rej, res){ var _this = this, _acquire = this.$1, _dispose = this.$2, _consume = this.$3; var cancel, cancelConsume = noop, resource, value, cont = noop; function Hook$done(){ cont(value); } function Hook$rec(x){ rec(wrapException(x, _this)); } function Hook$dispose(){ var disposal; try{ disposal = _dispose(resource); }catch(e){ return Hook$rec(e); } if(!isFuture(disposal)){ return Hook$rec(invalidDisposal(disposal, _dispose, resource)); } cancel = Hook$cancelDisposal; disposal._interpret(Hook$rec, Hook$disposalRejected, Hook$done); } function Hook$cancelConsumption(){ cancelConsume(); Hook$dispose(); Hook$cancelDisposal(); } function Hook$cancelDisposal(){ cont = noop; } function Hook$disposalRejected(x){ Hook$rec(new Error('The disposal Future rejected with ' + show(x))); } function Hook$consumptionException(x){ cont = Hook$rec; value = x; Hook$dispose(); } function Hook$consumptionRejected(x){ cont = rej; value = x; Hook$dispose(); } function Hook$consumptionResolved(x){ cont = res; value = x; Hook$dispose(); } function Hook$consume(x){ resource = x; var consumption; try{ consumption = _consume(resource); }catch(e){ return Hook$consumptionException(e); } if(!isFuture(consumption)){ return Hook$consumptionException(invalidConsumption(consumption, _consume, resource)); } cancel = Hook$cancelConsumption; cancelConsume = consumption._interpret( Hook$consumptionException, Hook$consumptionRejected, Hook$consumptionResolved ); } var cancelAcquire = _acquire._interpret(Hook$rec, rej, Hook$consume); cancel = cancel || cancelAcquire; return function Hook$fork$cancel(){ rec = raise; cancel(); }; }); export function hook(acquire){ var context1 = application1(hook, future, arguments); return function hook(dispose){ var context2 = application(2, hook, func, arguments, context1); return function hook(consume){ var context3 = application(3, hook, func, arguments, context2); return new Hook(context3, acquire, dispose, consume); }; }; } ================================================ FILE: src/internal/const.js ================================================ export var FL = { alt: 'fantasy-land/alt', ap: 'fantasy-land/ap', bimap: 'fantasy-land/bimap', chain: 'fantasy-land/chain', chainRec: 'fantasy-land/chainRec', map: 'fantasy-land/map', of: 'fantasy-land/of', zero: 'fantasy-land/zero', }; export var ordinal = ['first', 'second', 'third', 'fourth', 'fifth']; export var namespace = 'fluture'; export var name = 'Future'; export var version = 5; export var $$type = namespace + '/' + name + '@' + version; ================================================ FILE: src/internal/debug.js ================================================ import {ordinal} from './const.js'; import {cons} from './list.js'; /* c8 ignore next */ var captureStackTrace = Error.captureStackTrace || captureStackTraceFallback; var _debug = debugHandleNone; export {captureStackTrace}; export function debugMode(debug){ _debug = debug ? debugHandleAll : debugHandleNone; } export function debugHandleNone(x){ return x; } export function debugHandleAll(x, fn, a, b, c){ return fn(a, b, c); } export function debug(x, fn, a, b, c){ return _debug(x, fn, a, b, c); } export function captureContext(previous, tag, fn){ return debug(previous, debugCaptureContext, previous, tag, fn); } export function debugCaptureContext(previous, tag, fn){ var context = {tag: tag, name: ' from ' + tag + ':'}; captureStackTrace(context, fn); return cons(context, previous); } export function captureApplicationContext(context, n, f){ return debug(context, debugCaptureApplicationContext, context, n, f); } export function debugCaptureApplicationContext(context, n, f){ return debugCaptureContext(context, ordinal[n - 1] + ' application of ' + f.name, f); } export function captureStackTraceFallback(x){ var e = new Error; if(typeof e.stack === 'string'){ x.stack = x.name + '\n' + e.stack.split('\n').slice(1).join('\n'); /* c8 ignore next 3 */ }else{ x.stack = x.name; } } ================================================ FILE: src/internal/error.js ================================================ import {show} from './utils.js'; import {ordinal, namespace, name, version} from './const.js'; import type from 'sanctuary-type-identifiers'; import {nil, cat} from './list.js'; import {captureStackTrace} from './debug.js'; function showArg(x){ return show(x) + ' :: ' + type.parse(type(x)).name; } export function error(message){ return new Error(message); } export function typeError(message){ return new TypeError(message); } export function invalidArgument(it, at, expected, actual){ return typeError( it + '() expects its ' + ordinal[at] + ' argument to ' + expected + '.' + '\n Actual: ' + showArg(actual) ); } export function invalidArgumentOf(expected){ return function(it, at, actual){ return invalidArgument(it, at, expected, actual); }; } export function invalidContext(it, actual){ return typeError( it + '() was invoked outside the context of a Future. You might want to use' + ' a dispatcher instead\n Called on: ' + show(actual) ); } export function invalidArity(f, args){ return new TypeError( f.name + '() expects to be called with a single argument per invocation\n' + ' Saw: ' + args.length + ' arguments' + Array.prototype.slice.call(args).map(function(arg, i){ return '\n ' + ( ordinal[i] ? ordinal[i].charAt(0).toUpperCase() + ordinal[i].slice(1) : 'Argument ' + String(i + 1) ) + ': ' + showArg(arg); }).join('') ); } function invalidNamespace(m, x){ return ( 'The Future was not created by ' + namespace + '. ' + 'Make sure you transform other Futures to ' + namespace + ' Futures. ' + 'Got ' + (x ? ('a Future from ' + x) : 'an unscoped Future') + '.' + '\n See: https://github.com/fluture-js/Fluture#casting-futures' ); } function invalidVersion(m, x){ return ( 'The Future was created by ' + (x < version ? 'an older' : 'a newer') + ' version of ' + namespace + '. ' + 'This means that one of the sources which creates Futures is outdated. ' + 'Update this source, or transform its created Futures to be compatible.' + '\n See: https://github.com/fluture-js/Fluture#casting-futures' ); } export function invalidFuture(desc, m, s){ var id = type.parse(type(m)); var info = id.name === name ? '\n' + ( id.namespace !== namespace ? invalidNamespace(m, id.namespace) : id.version !== version ? invalidVersion(m, id.version) : 'Nothing seems wrong. Contact the Fluture maintainers.') : ''; return typeError( desc + ' to be a valid Future.' + info + '\n' + ' Actual: ' + show(m) + ' :: ' + id.name + (s || '') ); } export function invalidFutureArgument(it, at, m, s){ return invalidFuture(it + '() expects its ' + ordinal[at] + ' argument', m, s); } export function ensureError(value, fn){ var message; try{ if(value instanceof Error) return value; message = 'A Non-Error was thrown from a Future: ' + show(value); }catch (_){ message = 'Something was thrown from a Future, but it could not be converted to String'; } var e = error(message); captureStackTrace(e, fn); return e; } export function assignUnenumerable(o, prop, value){ Object.defineProperty(o, prop, {value: value, writable: true, configurable: true}); } export function wrapException(caught, callingFuture){ var origin = ensureError(caught, wrapException); var context = cat(origin.context || nil, callingFuture.context); var e = error(origin.message); assignUnenumerable(e, 'future', origin.future || callingFuture); assignUnenumerable(e, 'reason', origin.reason || origin); assignUnenumerable(e, 'stack', e.reason.stack); return withExtraContext(e, context); } export function withExtraContext(e, context){ assignUnenumerable(e, 'context', context); assignUnenumerable(e, 'stack', e.stack + contextToStackTrace(context)); return e; } export function contextToStackTrace(context){ var stack = '', tail = context; while(tail !== nil){ stack = stack + '\n' + tail.head.stack; tail = tail.tail; } return stack; } ================================================ FILE: src/internal/iteration.js ================================================ import {isObject, isBoolean} from './predicates.js'; export function Next(x){ return {done: false, value: x}; } export function Done(x){ return {done: true, value: x}; } export function isIteration(x){ return isObject(x) && isBoolean(x.done); } ================================================ FILE: src/internal/list.js ================================================ export function List(head, tail){ this.head = head; this.tail = tail; } List.prototype.toJSON = function(){ return toArray(this); }; export var nil = new List(null, null); nil.tail = nil; export function isNil(list){ return list.tail === list; } // cons :: (a, List a) -> List a // -- O(1) append operation export function cons(head, tail){ return new List(head, tail); } // reverse :: List a -> List a // -- O(n) list reversal export function reverse(xs){ var ys = nil, tail = xs; while(!isNil(tail)){ ys = cons(tail.head, ys); tail = tail.tail; } return ys; } // cat :: (List a, List a) -> List a // -- O(n) list concatenation export function cat(xs, ys){ var zs = ys, tail = reverse(xs); while(!isNil(tail)){ zs = cons(tail.head, zs); tail = tail.tail; } return zs; } // toArray :: List a -> Array a // -- O(n) list to Array export function toArray(xs){ var tail = xs, arr = []; while(!isNil(tail)){ arr.push(tail.head); tail = tail.tail; } return arr; } ================================================ FILE: src/internal/parallel.js ================================================ import {noop} from './utils.js'; import {createTransformation, Future, crash, reject, resolve} from '../future.js'; function Eager(future){ var _this = this; _this.rec = noop; _this.rej = noop; _this.res = noop; _this.crashed = false; _this.rejected = false; _this.resolved = false; _this.value = null; _this.cancel = future._interpret(function Eager$crash(x){ _this.value = x; _this.crashed = true; _this.cancel = noop; _this.rec(x); }, function Eager$reject(x){ _this.value = x; _this.rejected = true; _this.cancel = noop; _this.rej(x); }, function Eager$resolve(x){ _this.value = x; _this.resolved = true; _this.cancel = noop; _this.res(x); }); } Eager.prototype = Object.create(Future.prototype); Eager.prototype._interpret = function Eager$interpret(rec, rej, res){ if(this.crashed) rec(this.value); else if(this.rejected) rej(this.value); else if(this.resolved) res(this.value); else{ this.rec = rec; this.rej = rej; this.res = res; } return this.cancel; }; export function earlyCrash(early, x){ early(crash(x)); } export function earlyReject(early, x){ early(reject(x)); } export function earlyResolve(early, x){ early(resolve(x)); } export function createParallelTransformation(name, rec, rej, res, prototype){ var ParallelTransformation = createTransformation(1, name, Object.assign({ run: function Parallel$run(early){ var eager = new Eager(this.$1); var transformation = new ParallelTransformation(this.context, eager); function Parallel$early(m){ early(m, transformation) } transformation.cancel = eager._interpret( function Parallel$rec(x){ rec(Parallel$early, x) }, function Parallel$rej(x){ rej(Parallel$early, x) }, function Parallel$res(x){ res(Parallel$early, x) } ); return transformation; }, }, prototype)); return ParallelTransformation; } ================================================ FILE: src/internal/predicates.js ================================================ import {FL} from './const.js'; export function isFunction(f){ return typeof f === 'function'; } export function isThenable(m){ return m instanceof Promise || m != null && isFunction(m.then); } export function isBoolean(f){ return typeof f === 'boolean'; } export function isNumber(f){ return typeof f === 'number'; } export function isUnsigned(n){ return (n === Infinity || isNumber(n) && n > 0 && n % 1 === 0); } export function isObject(o){ return o !== null && typeof o === 'object'; } export function isIterator(i){ return isObject(i) && isFunction(i.next); } export function isArray(x){ return Array.isArray(x); } export function hasMethod(method, x){ return x != null && isFunction(x[method]); } export function isFunctor(x){ return hasMethod(FL.map, x); } export function isAlt(x){ return isFunctor(x) && hasMethod(FL.alt, x); } export function isApply(x){ return isFunctor(x) && hasMethod(FL.ap, x); } export function isBifunctor(x){ return isFunctor(x) && hasMethod(FL.bimap, x); } export function isChain(x){ return isApply(x) && hasMethod(FL.chain, x); } ================================================ FILE: src/internal/timing.js ================================================ export var Undetermined = 0; export var Synchronous = 1; export var Asynchronous = 2; ================================================ FILE: src/internal/utils.js ================================================ export {default as show} from 'sanctuary-show'; /* c8 ignore next */ var setImmediate = typeof setImmediate === 'undefined' ? setImmediateFallback : setImmediate; export function noop(){} export function moop(){ return this } export function call(f, x){ return f(x) } export function setImmediateFallback(f, x){ return setTimeout(f, 0, x); } export function raise(x){ setImmediate(function rethrowErrorDelayedToEscapePromiseCatch(){ throw x; }); } ================================================ FILE: src/lastly.js ================================================ import {AndTransformation} from './and.js'; import { application, application1, createTransformation, future, Reject, Resolve, } from './future.js'; export var LastlyTransformation = createTransformation(1, 'lastly', { rejected: function LastlyAction$rejected(x){ return this.$1._transform(new AndTransformation(this.context, new Reject(this.context, x))); }, resolved: function LastlyAction$resolved(x){ return this.$1._transform(new AndTransformation(this.context, new Resolve(this.context, x))); }, }); export function lastly(cleanup){ var context1 = application1(lastly, future, arguments); return function lastly(program){ var context2 = application(2, lastly, future, arguments, context1); return program._transform(new LastlyTransformation(context2, cleanup)); }; } ================================================ FILE: src/map-rej.js ================================================ import {call} from './internal/utils.js'; import {createTransformation, Reject, application1, application, future, func} from './future.js'; export var MapRejTransformation = createTransformation(1, 'mapRej', { rejected: function MapRejTransformation$rejected(x){ return new Reject(this.context, call(this.$1, x)); }, }); export function mapRej(f){ var context1 = application1(mapRej, func, arguments); return function mapRej(m){ var context2 = application(2, mapRej, future, arguments, context1); return m._transform(new MapRejTransformation(context2, f)); }; } ================================================ FILE: src/map.js ================================================ import {FL} from './internal/const.js'; import {invalidArgumentOf} from './internal/error.js'; import {isFunctor} from './internal/predicates.js'; import {isFuture, MapTransformation, application1, application, func} from './future.js'; export var functor = {pred: isFunctor, error: invalidArgumentOf('have Functor implemented')}; export function map(f){ var context1 = application1(map, func, arguments); return function map(m){ var context2 = application(2, map, functor, arguments, context1); return isFuture(m) ? m._transform(new MapTransformation(context2, f)) : m[FL.map](f); }; } ================================================ FILE: src/node.js ================================================ import {wrapException} from './internal/error.js'; import {noop, call} from './internal/utils.js'; import {createInterpreter, application1, func} from './future.js'; export var Node = createInterpreter(1, 'node', function Node$interpret(rec, rej, res){ function Node$done(err, val){ cont = err ? function EncaseN3$rej(){ open = false; rej(err); } : function EncaseN3$res(){ open = false; res(val); }; if(open){ cont(); } } var open = false, cont = function(){ open = true }; try{ call(this.$1, Node$done); }catch(e){ rec(wrapException(e, this)); open = false; return noop; } cont(); return function Node$cancel(){ open = false }; }); export function node(f){ return new Node(application1(node, func, arguments), f); } ================================================ FILE: src/pap.js ================================================ import {createParallelTransformation, earlyCrash, earlyReject} from './internal/parallel.js'; import {noop} from './internal/utils.js'; import {typeError} from './internal/error.js'; import {isFunction} from './internal/predicates.js'; import {show} from './internal/utils.js'; import {MapTransformation, application1, application, future} from './future.js'; export var ParallelApTransformation = createParallelTransformation('pap', earlyCrash, earlyReject, noop, { resolved: function ParallelApTransformation$resolved(f){ if(isFunction(f)) return this.$1._transform(new MapTransformation(this.context, f)); throw typeError( 'pap expects the second Future to resolve to a Function\n' + ' Actual: ' + show(f) ); }, }); export function pap(mx){ var context1 = application1(pap, future, arguments); return function pap(mf){ var context2 = application(2, pap, future, arguments, context1); return mf._transform(new ParallelApTransformation(context2, mx)); }; } ================================================ FILE: src/par.js ================================================ import type from 'sanctuary-type-identifiers'; import {FL, namespace, version} from './internal/const.js'; import {invalidFutureArgument} from './internal/error.js'; import {captureContext} from './internal/debug.js'; import {nil} from './internal/list.js'; import {never, resolve, isFuture, MapTransformation} from './future.js'; import {ParallelApTransformation} from './pap.js'; import {RaceTransformation} from './race.js'; export function ConcurrentFuture(sequential){ this.sequential = sequential; } ConcurrentFuture.prototype = Object.create(Par.prototype); export function Par(sequential){ if(!isFuture(sequential)) throw invalidFutureArgument(Par.name, 0, sequential); return new ConcurrentFuture(sequential); } var $$type = namespace + '/ConcurrentFuture@' + version; var zeroInstance = new ConcurrentFuture(never); // Compliance with sanctuary-type-identifiers versions 1 and 2. // To prevent sanctuary-type-identifiers version 3 from identifying // 'Par' as being of the type denoted by $$type, we ensure that // Par.constructor.prototype is equal to Par. Par['@@type'] = $$type; Par.constructor = {prototype: Par}; Par[FL.of] = function Par$of(x){ return new ConcurrentFuture(resolve(x)); }; Par[FL.zero] = function Par$zero(){ return zeroInstance; }; Par.prototype['@@type'] = $$type; Par.prototype['@@show'] = function Par$show(){ return this.toString(); }; Par.prototype.toString = function Par$toString(){ return 'Par (' + this.sequential.toString() + ')'; }; Par.prototype[FL.map] = function Par$FL$map(f){ var context = captureContext( nil, 'a Fantasy Land dispatch to map via ConcurrentFuture', Par$FL$map ); return new ConcurrentFuture(this.sequential._transform(new MapTransformation(context, f))); }; Par.prototype[FL.ap] = function Par$FL$ap(other){ var context = captureContext( nil, 'a Fantasy Land dispatch to ap via ConcurrentFuture', Par$FL$ap ); return new ConcurrentFuture(other.sequential._transform( new ParallelApTransformation(context, this.sequential) )); }; Par.prototype[FL.alt] = function Par$FL$alt(other){ var context = captureContext( nil, 'a Fantasy Land dispatch to alt via ConcurrentFuture', Par$FL$alt ); return new ConcurrentFuture(other.sequential._transform( new RaceTransformation(context, this.sequential) )); }; export function isParallel(x){ return x instanceof ConcurrentFuture || type(x) === $$type; } ================================================ FILE: src/parallel.js ================================================ import {wrapException, invalidArgumentOf} from './internal/error.js'; import {isArray} from './internal/predicates.js'; import {noop} from './internal/utils.js'; import { createInterpreter, isFuture, resolve, application1, positiveInteger, application, } from './future.js'; function isFutureArray(xs){ if(!isArray(xs)) return false; for(var i = 0; i < xs.length; i++){ if(!isFuture(xs[i])) return false; } return true; } export var futureArray = { pred: isFutureArray, error: invalidArgumentOf('be an Array of valid Futures'), }; export var Parallel = createInterpreter(2, 'parallel', function Parallel$interpret(rec, rej, res){ var _this = this, futures = this.$2, length = futures.length; var max = Math.min(this.$1, length), cancels = new Array(length), out = new Array(length); var cursor = 0, running = 0, blocked = false, cont = noop; function Parallel$cancel(){ rec = noop; rej = noop; res = noop; cursor = length; for(var n = 0; n < length; n++) cancels[n] && cancels[n](); } function Parallel$run(idx){ running++; cancels[idx] = futures[idx]._interpret(function Parallel$rec(e){ cont = rec; cancels[idx] = noop; Parallel$cancel(); cont(wrapException(e, _this)); }, function Parallel$rej(reason){ cont = rej; cancels[idx] = noop; Parallel$cancel(); cont(reason); }, function Parallel$res(value){ cancels[idx] = noop; out[idx] = value; running--; if(cursor === length && running === 0) res(out); else if(blocked) Parallel$drain(); }); } function Parallel$drain(){ blocked = false; while(cursor < length && running < max) Parallel$run(cursor++); blocked = true; } Parallel$drain(); return Parallel$cancel; }); var emptyArray = resolve([]); export function parallel(max){ var context1 = application1(parallel, positiveInteger, arguments); return function parallel(ms){ var context2 = application(2, parallel, futureArray, arguments, context1); return ms.length === 0 ? emptyArray : new Parallel(context2, max, ms); }; } ================================================ FILE: src/promise.js ================================================ import {application1, future} from './future.js'; export function promise(m){ application1(promise, future, arguments); return new Promise(function promise$computation(res, rej){ m._interpret(rej, rej, res); }); } ================================================ FILE: src/race.js ================================================ import { createParallelTransformation, earlyCrash, earlyReject, earlyResolve, } from './internal/parallel.js'; import {application1, application, future} from './future.js'; export var RaceTransformation = createParallelTransformation('race', earlyCrash, earlyReject, earlyResolve, {}); export function race(left){ var context1 = application1(race, future, arguments); return function race(right){ var context2 = application(2, race, future, arguments, context1); return right._transform(new RaceTransformation(context2, left)); }; } ================================================ FILE: src/reject-after.js ================================================ import { any, application, application1, createInterpreter, never, positiveInteger, } from './future.js'; export var RejectAfter = createInterpreter(2, 'rejectAfter', function RejectAfter$interpret(rec, rej){ var id = setTimeout(rej, this.$1, this.$2); return function RejectAfter$cancel(){ clearTimeout(id) }; }); RejectAfter.prototype.extractLeft = function RejectAfter$extractLeft(){ return [this.$2]; }; function alwaysNever(_){ return never; } export function rejectAfter(time){ var context1 = application1(rejectAfter, positiveInteger, arguments); return time === Infinity ? alwaysNever : (function rejectAfter(value){ var context2 = application(2, rejectAfter, any, arguments, context1); return new RejectAfter(context2, time, value); }); } ================================================ FILE: src/seq.js ================================================ import {invalidArgumentOf} from './internal/error.js'; import {application1} from './future.js'; import {isParallel} from './par.js'; var parallel = {pred: isParallel, error: invalidArgumentOf('be a ConcurrentFuture')}; export function seq(par){ application1(seq, parallel, arguments); return par.sequential; } ================================================ FILE: src/swap.js ================================================ import {createTransformation, Reject, Resolve, application1, future} from './future.js'; export var SwapTransformation = createTransformation(0, 'swap', { resolved: function SwapTransformation$resolved(x){ return new Reject(this.context, x); }, rejected: function SwapTransformation$rejected(x){ return new Resolve(this.context, x); }, }); export function swap(m){ var context = application1(swap, future, arguments); return m._transform(new SwapTransformation(context)); } ================================================ FILE: src/value.js ================================================ import {error} from './internal/error.js'; import {raise, show} from './internal/utils.js'; import {application1, application, func, future} from './future.js'; export function value(res){ var context1 = application1(value, func, arguments); return function value(m){ application(2, value, future, arguments, context1); function value$rej(x){ raise(error( 'Future#value was called on a rejected Future\n' + ' Rejection: ' + show(x) + '\n' + ' Future: ' + show(m) )); } return m._interpret(raise, value$rej, res); }; } ================================================ FILE: test/arbitraries.js ================================================ import jsv from 'jsverify'; import {Future, resolve, reject, Par, seq, extractLeft, extractRight} from '../index.js'; const noop = () => {}; const show = m => m.toString(); export const nil = jsv.elements([null, undefined]); const immediatelyResolve = x => { const m = Future((rej, res) => { setImmediate(res, x); return noop }); m.extractRight = () => [x]; return m; }; const immediatelyReject = x => { const m = Future((rej) => { setImmediate(rej, x); return noop }); m.extractLeft = () => [x]; return m; }; export function AsyncResolvedFutureArb (arb){ return arb.smap(immediatelyResolve, extractRight, show); } export function AsyncRejectedFutureArb (arb){ return arb.smap(immediatelyReject, extractLeft, show); } export function ResolvedFutureArb (arb){ return arb.smap(resolve, extractRight, show); } export function RejectedFutureArb (arb){ return arb.smap(reject, extractLeft, show); } export function FutureArb (larb, rarb){ return jsv.oneof( RejectedFutureArb(larb), ResolvedFutureArb(rarb), AsyncRejectedFutureArb(larb), AsyncResolvedFutureArb(rarb) ); } export const { any, anyFuture, anyRejectedFuture, anyResolvedFuture, anyNonFuture, anyFunction, } = jsv.letrec(tie => ({ anyRejectedFuture: jsv.oneof(AsyncRejectedFutureArb(tie('any')), RejectedFutureArb(tie('any'))), anyResolvedFuture: jsv.oneof(AsyncResolvedFutureArb(tie('any')), ResolvedFutureArb(tie('any'))), anyFuture: jsv.oneof(tie('anyRejectedFuture'), tie('anyResolvedFuture')), anyFunction: jsv.fn(tie('any')), anyNonFuture: jsv.oneof( jsv.number, jsv.string, jsv.bool, jsv.falsy, jsv.constant(new Error('Kapot')), jsv.constant(Future), jsv.constant(Par), tie('anyFunction') ), any: jsv.oneof( tie('anyNonFuture'), tie('anyFuture') ), })); export const anyParallel = anyFuture.smap(Par, seq, show); ================================================ FILE: test/assertions.js ================================================ import show from 'sanctuary-show'; import type from 'sanctuary-type-identifiers'; import {Future, isFuture} from '../index.js'; import {strictEqual, deepStrictEqual} from 'assert'; const states = ['pending', 'crashed', 'rejected', 'resolved']; export const equality = a => b => { strictEqual(show(a), show(b)); deepStrictEqual(a, b); return true; }; export const future = x => { equality(isFuture(x))(true); equality(type(x))(Future['@@type']); return true; }; export function makeEquivalence (equals){ return function equivalence (ma){ return function (mb){ let astate = 0, bstate = 0, val; return new Promise(function (pass, fail){ future(ma); future(mb); function twice (m, x, s1, s2){ fail(new Error( 'A Future ' + states[s2] + ' after already having ' + states[s1] + '.\n' + ' First: Future({ <' + states[s1] + '> ' + show(val) + ' })\n' + ' Second: Future({ <' + states[s1] + '> ' + show(x) + ' })\n' + ' Future: ' + m.toString() )); } function assertInnerEqual (a, b){ if(astate === bstate){ if(isFuture(a) && isFuture(b)){ equivalence(a)(b).then(pass, fail); return; } try { equals(a)(b); pass(true); } catch (e){ inequivalent('The inner values are not equal: ' + e.message); } }else{ inequivalent( 'One Future ' + states[astate] + ', and the other Future ' + states[bstate] ); } function inequivalent (message){ fail(new Error( '\n ' + ma.toString() + ' :: Future({ <' + states[astate] + '> ' + show(a) + ' })' + '\n is not equivalent to:\n ' + mb.toString() + ' :: Future({ <' + states[bstate] + '> ' + show(b) + ' })\n\n' + message )); } } ma._interpret(function (x){ if(astate > 0) twice(ma, x, astate, 1); else { astate = 1; if(bstate > 0) assertInnerEqual(x, val); else val = x; } }, function (x){ if(astate > 0) twice(ma, x, astate, 2); else { astate = 2; if(bstate > 0) assertInnerEqual(x, val); else val = x; } }, function (x){ if(astate > 0) twice(ma, x, astate, 3); else { astate = 3; if(bstate > 0) assertInnerEqual(x, val); else val = x; } }); mb._interpret(function (x){ if(bstate > 0) twice(mb, x, bstate, 1); else { bstate = 1; if(astate > 0) assertInnerEqual(val, x); else val = x; } }, function (x){ if(bstate > 0) twice(mb, x, bstate, 2); else { bstate = 2; if(astate > 0) assertInnerEqual(val, x); else val = x; } }, function (x){ if(bstate > 0) twice(mb, x, bstate, 3); else { bstate = 3; if(astate > 0) assertInnerEqual(val, x); else val = x; } }); }); }; }; } export const equivalence = makeEquivalence(equality); ================================================ FILE: test/build/main.js ================================================ import {noop, eq, test} from '../util/util.js'; import * as Fluture from '../../index.js'; import require from './require.js'; const Future = require('../../index.cjs'); test('exports the Future constructor by default', function (){ eq(typeof Future, 'function'); eq(Future(noop) instanceof Future, true); }); test('has a Future property that refers to itself for import destructuring', function (){ eq(Future.Future, Future); }); Object.keys(Fluture).forEach(key => { test('has a ' + key + ' property of type ' + typeof Fluture[key], function (){ eq(typeof Future[key], typeof Fluture[key]); }); }); test('exports Future with the correct name property', function (){ eq(Future.name, Fluture.Future.name); }); ================================================ FILE: test/build/require.js ================================================ import {createRequire} from 'module'; export default createRequire(import.meta.url); ================================================ FILE: test/integration/main.js ================================================ import { Future, after, ap, both, cache, chain, fork, map, parallel, race, rejectAfter, resolve, } from '../../index.js'; import {noop, error, assertResolved, eq, add, test} from '../util/util.js'; import {resolved, resolvedSlow} from '../util/futures.js'; function through (x, fs){ return fs.reduce(function (y, f){ return f(y); }, x); } test('is capable of joining', function (){ var m = through(resolve('a'), [ chain(function (x){ return chain(function (x){ return after(5)(x + 'c') })(after(5)(x + 'b')) }), chain(function (x){ return after(5)(x + 'd') }), chain(function (x){ return resolve(x + 'e') }), chain(function (x){ return after(5)(x + 'f') }), ]); return assertResolved(m, 'abcdef'); }); test('is capable of early termination', function (done){ var slow = Future(function (){ var id = setTimeout(done, 20, new Error('Not terminated')); return function (){ return clearTimeout(id) }; }); var m = through(slow, [race(slow), race(slow), race(slow), race(resolved)]); m._interpret(done, noop, noop); setTimeout(done, 40, null); }); test('cancels running actions when one early-terminates asynchronously', function (done){ var slow = Future(function (){ var id = setTimeout(done, 50, new Error('Not terminated')); return function (){ return clearTimeout(id) }; }); var m = through(slow, [race(slow), race(slow), race(slow), race(resolvedSlow)]); m._interpret(done, noop, noop); setTimeout(done, 100, null); }); test('does not run actions unnecessarily when one early-terminates synchronously', function (done){ var broken = Future(function (){ done(error) }); var m = through(resolvedSlow, [race(broken), race(broken), race(resolved)]); m._interpret(done, noop, function (){ return done() }); }); test('resolves the left-hand side first when running actions in parallel', function (){ var m = through(resolve(1), [ map(function (x){ return x }), chain(function (x){ return resolve(x) }), race(resolve(2)), ]); return assertResolved(m, 1); }); test('does not forget about actions to run after early termination', function (){ var m = through('a', [ after(100), race(after(20)('b')), map(function (x){ return (x + 'c') }), ]); return assertResolved(m, 'bc'); }); test('does not run early terminating actions twice, or cancel them', function (done){ var mock = Object.create(Future.prototype); mock._interpret = function (_, l, r){ return r(done()) || (function (){ return done(error) }) }; var m = through('a', [ after(30), map(function (x){ return (x + 'b') }), race(mock), ]); m._interpret(done, noop, noop); }); test('does not run concurrent computations twice', function (done){ var ran = false; var m = through(resolvedSlow, [ chain(function (){ return resolvedSlow }), race(Future(function (){ ran ? done(error) : (ran = true); return noop })), ]); m._interpret(done, done, function (){ return done() }); }); test('returns a cancel function which cancels all running actions', function (done){ var i = 0; var started = function (){ return void i++ }; var cancelled = function (){ return --i < 1 && done() }; var slow = Future(function (){ return started() || (function (){ return cancelled() }) }); var m = through(slow, [race(slow), race(slow), race(slow), race(slow)]); var cancel = m._interpret(done, noop, noop); eq(i, 5); cancel(); }); test('returns source when cast to String', function (){ const m = ap(resolve(22))(map(add)(resolve(20))); eq( String(m), 'ap (resolve (22)) (map (function (a){ return function (b){ return a + b } }) (resolve (20)))' ); }); test('returns an AST when cast to JSON', function (){ const m = ap(resolve(22))(map(add)(resolve(20))); eq( JSON.stringify(m), '{"$":"fluture/Future@5","kind":"interpreter","type":"transform","args":[' + '{"$":"fluture/Future@5","kind":"interpreter","type":"resolve","args":[20]},[' + '{"$":"fluture/Future@5","kind":"transformation","type":"ap","args":[' + '{"$":"fluture/Future@5","kind":"interpreter","type":"resolve","args":[22]}' + ']},' + '{"$":"fluture/Future@5","kind":"transformation","type":"map","args":[null]}' + ']' + ']}' ); }); test('does not produce issue #362', function (done){ const ma = cache(rejectAfter(1)(new Error)); const mb = map(noop)(parallel(Infinity)([ma, ma])); fork(() => done())(noop)(mb); }); test('does not produce issue #362 in a regular combinator', function (done){ const ma = cache(rejectAfter(1)(new Error)); const mb = map(noop)(both(ma)(ma)); fork(() => done())(noop)(mb); }); ================================================ FILE: test/prop/0.algebra.js ================================================ import show from 'sanctuary-show'; import {assertEqual, I, B, T, K} from '../util/util.js'; import {FutureArb, any as _x, anyFuture as _mx, f, g, _of, elements, property} from '../util/props.js'; import { alt, and, ap, bichain, bimap, cache, chain, chainRej, hook, lastly, map, mapRej, reject, resolve, swap, } from '../../index.js'; var _f = elements([f, g, I, resolve]); var _mf = _of(_f); var _fm = FutureArb(_f, _f).smap(function (m){ return function (x){ return bimap(T(x))(T(x))(m); }; }, function (f){ return bimap(K)(K)(f()); }, show); function eq (x){ return function (y){ return assertEqual(x, y); }; } property('alt associativity', _mx, _mx, _mx, function (a, b, c){ return eq(alt(a)(alt(b)(c)))(alt(alt(a)(b))(c)); }); property('alt distributivity with map', _mx, _mx, function (a, b){ return eq(map(f)(alt(a)(b)))(alt(map(f)(a))(map(f)(b))); }); property('and associativity', _mx, _mx, _mx, function (a, b, c){ return eq(and(a)(and(b)(c)))(and(and(a)(b))(c)); }); property('and distributivity with map', _mx, _mx, function (a, b){ return eq(map(f)(and(a)(b)))(and(map(f)(a))(map(f)(b))); }); property('ap composition using map', _mx, _mf, _mf, function (mx, mf, mg){ return eq(ap(mx)(ap(mf)(map(B)(mg))))(ap(ap(mx)(mf))(mg)); }); property('bichain associativity', _mx, _fm, _fm, function (m, f, g){ return eq(bichain(g)(g)(bichain(f)(f)(m)))(bichain(B(bichain(g)(g))(f))(B(bichain(g)(g))(f))(m)); }); property('bichain left identity on rejection', _x, _fm, _fm, function (x, f, g){ return eq(bichain(f)(g)(reject(x)))(f(x)); }); property('bichain left identity on resolution', _x, _fm, _fm, function (x, f, g){ return eq(bichain(f)(g)(resolve(x)))(g(x)); }); property('bichain right identity', _mx, function (m){ return eq(bichain(reject)(resolve)(m))(m); }); property('bimap identity', _mx, function (mx){ return eq(bimap(I)(I)(mx))(mx); }); property('bimap composition', _mx, _f, _f, _f, _f, function (mx, f, g, h, i){ return eq(bimap(B(f)(g))(B(h)(i))(mx))(bimap(f)(h)(bimap(g)(i)(mx))); }); property('cache idempotence', _mx, function (m){ return eq(cache(cache(m)))(cache(m)); }); property('chain associativity', _mx, _fm, _fm, function (m, f, g){ return eq(chain(g)(chain(f)(m)))(chain(B(chain(g))(f))(m)); }); property('chain left identity', _x, _fm, function (x, f){ return eq(chain(f)(resolve(x)))(f(x)); }); property('chain right identity', _mx, function (m){ return eq(chain(resolve)(m))(m); }); property('chainRej associativity', _mx, _fm, _fm, function (m, f, g){ return eq(chainRej(g)(chainRej(f)(m)))(chainRej(B(chainRej(g))(f))(m)); }); property('chainRej left identity', _x, _fm, function (x, f){ return eq(chainRej(f)(reject(x)))(f(x)); }); property('chainRej right identity', _mx, function (m){ return eq(chainRej(reject)(m))(m); }); property('hook identity', _mx, function (m){ return eq(hook(m)(resolve)(resolve))(m); }); property('lastly associativity', _mx, _mx, _mx, function (a, b, c){ return eq(lastly(a)(lastly(b)(c)))(lastly(lastly(a)(b))(c)); }); property('lastly distributivity with map', _mx, _mx, function (a, b){ return eq(map(f)(lastly(a)(b)))(lastly(map(f)(a))(map(f)(b))); }); property('map identity', _mx, function (m){ return eq(map(I)(m))(m); }); property('map composition', _mx, _f, _f, function (m, f, g){ return eq(map(B(f)(g))(m))(map(f)(map(g)(m))); }); property('mapRej identity', _mx, function (m){ return eq(mapRej(I)(m))(m); }); property('mapRej composition', _mx, _f, _f, function (m, f, g){ return eq(mapRej(B(f)(g))(m))(mapRej(f)(mapRej(g)(m))); }); property('resolve identity for ap', _mx, function (mx){ return eq(ap(mx)(resolve(I)))(mx); }); property('resolve homomorphism with ap', _x, function (x){ return eq(ap(resolve(x))(resolve(f)))(resolve(f(x))); }); property('resolve interchange with ap', _x, _mf, function (x, mf){ return eq(ap(resolve(x))(mf))(ap(mf)(resolve(T(x)))); }); property('swap self inverse', _mx, function (m){ return eq(swap(swap(m)))(m); }); ================================================ FILE: test/prop/0.fantasy-land.js ================================================ import FL from 'fantasy-laws'; import Z from 'sanctuary-type-classes'; import show from 'sanctuary-show'; import {Future, bimap} from '../../index.js'; import {assertEqual as eq, I, B, T, K, noop, STACKSIZE, test} from '../util/util.js'; import { FutureArb, _of, any as _x, anyFuture as _mx, constant as _k, elements, f, g, nat, property, suchthat, } from '../util/props.js'; var of = function (x){ return Z.of(Future, x); }; var _f = elements([f, g, I, of]); var _mf = _of(_f); var _fm = FutureArb(_f, _f).smap(function (m){ return function (x){ return bimap(T(x))(T(x))(m); }; }, function (f){ return bimap(K)(K)(f()); }, show); function testLaw (laws, typeclass, name){ var args = Array.from(arguments).slice(3); test(`${typeclass} ${name}`, laws[name].apply(null, args)); } testLaw(FL.Functor(eq), 'Functor', 'identity', _mx); testLaw(FL.Functor(eq), 'Functor', 'composition', _mx, _f, _f); testLaw(FL.Alt(eq), 'Alt', 'associativity', _mx, _mx, _mx); testLaw(FL.Alt(eq), 'Alt', 'distributivity', _mx, _mx, _f); testLaw(FL.Bifunctor(eq), 'Bifunctor', 'identity', _mx); testLaw(FL.Bifunctor(eq), 'Bifunctor', 'composition', _mx, _f, _f, _f, _f); testLaw(FL.Apply(eq), 'Apply', 'composition', _mf, _mf, _mx); testLaw(FL.Applicative(eq, Future), 'Applicative', 'identity', _mx); testLaw(FL.Applicative(eq, Future), 'Applicative', 'homomorphism', _f, _x); testLaw(FL.Applicative(eq, Future), 'Applicative', 'interchange', _mf, _x); testLaw(FL.Chain(eq), 'Chain', 'associativity', _mx, _fm, _fm); testLaw( FL.ChainRec(eq, Future), 'ChainRec', 'equivalence', _k(function (v){ return v < 1 }), _k(B(of)(function (v){ return v - 1 })), _k(of), suchthat(nat, function (x){ return x < 100 }) ); test('ChainRec stack-safety', function (){ var p = function (v){ return v > (STACKSIZE + 1) }; var d = of; var n = B(of)(function (v){ return v + 1 }); var a = Z.chainRec(Future, function (l, r, v){ return p(v) ? Z.map(r, d(v)) : Z.map(l, n(v)) }, 0); a._interpret(noop, noop, noop); }); testLaw(FL.Monad(eq, Future), 'Monad', 'leftIdentity', _fm, _mx); testLaw(FL.Monad(eq, Future), 'Monad', 'rightIdentity', _mx); property('map derived from ap and of', _mx, _f, function (m, f){ return eq(Z.map(f, m), Z.ap(of(f), m)); }); property('map derived from chain and of', _mx, _f, function (m, f){ return eq(Z.map(f, m), Z.chain(B(of)(f), m)); }); property('map derived from bimap', _mx, _f, function (m, f){ return eq(Z.map(f, m), Z.bimap(I, f, m)); }); property('ap derived from chain and map', _mx, _mf, function (mx, mf){ return eq(Z.ap(mf, mx), Z.chain(function (f){ return Z.map(f, mx) }, mf)); }); ================================================ FILE: test/prop/1.fantasy-libs.js ================================================ import * as R from 'ramda'; import Z from 'sanctuary-type-classes'; import {Future, ap, alt, map, bimap, chain, resolve, reject} from '../../index.js'; import {assertEqual} from '../util/util.js'; import {any, property, FutureArb, string, number, constant, anyFuture, oneof} from '../util/props.js'; function bang (x){ return x + '!'; } function compose (f, g){ return function (x){ return f(g(x)); }; } function square (x){ return x * x; } var stringNumberFuture = FutureArb(string, number); var stringSquareFuture = FutureArb(string, constant(square)); var make = oneof(constant(resolve), constant(reject)); property('Z.of(Future, x) = resolve(x)', any, function (x){ return assertEqual(Z.of(Future, x), resolve(x)); }); property('R.ap(mf, mx) = ap(mx)(mf)', stringSquareFuture, stringNumberFuture, function (mf, mx){ return assertEqual(R.ap(mf, mx), ap(mx)(mf)); }); property('Z.ap(mf, mx) = ap(mx)(mf)', stringSquareFuture, stringNumberFuture, function (mf, mx){ return assertEqual(Z.ap(mf, mx), ap(mx)(mf)); }); property('Z.alt(a, b) = alt(b)(a)', anyFuture, anyFuture, function (a, b){ return assertEqual(Z.alt(a, b), alt(b)(a)); }); property('R.map(f, mx) = map(f)(mx)', stringNumberFuture, function (mx){ return assertEqual(R.map(square, mx), map(square)(mx)); }); property('Z.map(f, mx) = map(f, mx)', stringNumberFuture, function (mx){ return assertEqual(Z.map(square, mx), map(square)(mx)); }); property('Z.bimap(f, g, mx) = bimap(f)(g)(mx)', stringNumberFuture, function (mx){ return assertEqual(Z.bimap(bang, square, mx), bimap(bang)(square)(mx)); }); property('R.chain(f, mx) = chain(f)(mx)', make, stringNumberFuture, function (g, mx){ var f = compose(f, square); return assertEqual(R.chain(f, mx), chain(f)(mx)); }); property('Z.chain(f, mx) = chain(f)(mx)', make, stringNumberFuture, function (g, mx){ var f = compose(f, square); return assertEqual(Z.chain(f, mx), chain(f)(mx)); }); ================================================ FILE: test/prop/2.arbitrary.js ================================================ import {assertEqual, I, B, K} from '../util/util.js'; import { any, anyFuture, anyRejectedFuture, anyResolvedFuture, constant, f, g, oneof, property, } from '../util/props.js'; import { after, and, bichain, bimap, chain, chainRej, coalesce, go, map, mapRej, reject, rejectAfter, resolve, swap, } from '../../index.js'; var make = oneof(constant(resolve), constant(reject)); function eq (a){ return function (b){ return assertEqual(a, b); }; } property('bichain(reject)(B(mk)(f))(m) = chain(B(mk)(f))(m)', make, anyFuture, function (mk, m){ return eq(bichain(reject)(B(mk)(f))(m))(chain(B(mk)(f))(m)); }); property('bichain(B(mk)(f))(resolve)(m) = chainRej(B(mk)(f))(m)', make, anyFuture, function (mk, m){ return eq(bichain(B(mk)(f))(resolve)(m))(chainRej(B(mk)(f))(m)); }); property('bichain(B(mk)(f))(B(mk)(g))(m) = chain(I)(coalesce(B(mk)(f))(B(mk)(g))(m))', make, anyFuture, function (mk, m){ return eq(bichain(B(mk)(f))(B(mk)(g))(m))(chain(I)(coalesce(B(mk)(f))(B(mk)(g))(m))); }); property('swap(m) = bichain(resolve)(reject)(m)', anyFuture, function (m){ return eq(swap(m))(bichain(resolve)(reject)(m)); }); property('swap(resolve(x)) = reject(x)', any, function (x){ return eq(swap(resolve(x)))(reject(x)); }); property('swap(reject(x)) = resolve(x)', any, function (x){ return eq(swap(reject(x)))(resolve(x)); }); property('Resolved m => chainRej(B(mk)(f))(m) = m', make, anyResolvedFuture, function (mk, m){ return eq(chainRej(B(mk)(f))(m))(m); }); property('Rejected m => chainRej(B(mk)(f))(m) = chain(B(mk)(f))(swap(m))', make, anyRejectedFuture, function (mk, m){ return eq(chainRej(B(mk)(f))(m))(chain(B(mk)(f))(swap(m))); }); property('Resolved m => chain(B(mk)(f))(m) = chainRej(B(mk)(f))(swap(m))', make, anyResolvedFuture, function (mk, m){ return eq(chain(B(mk)(f))(m))(chainRej(B(mk)(f))(swap(m))); }); property('after(1)(x) = resolve(x)', any, function (x){ return eq(after(1)(x))(resolve(x)); }); property('and(a)(b) = chain(K(a))(b)', anyFuture, anyFuture, function (a, b){ return eq(and(a)(b))(chain(K(a))(b)); }); property('coalesce(f)(g)(m) = chainRej(B(resolve)(f))(map(g)(m))', anyFuture, function (m){ return eq(coalesce(f)(g)(m))(chainRej(B(resolve)(f))(map(g)(m))); }); property('go(function*(){ return f(yield m) }) = map(f)(m)', anyFuture, function (m){ return eq(go(function*(){ return f(yield m) }))(map(f)(m)); }); property('mapRej(f)(m) = chainRej(B(reject)(f))(m)', anyFuture, function (m){ return eq(mapRej(f)(m))(chainRej(B(reject)(f))(m)); }); property('mapRej(f)(m) = bimap(f)(I)(m)', anyFuture, function (m){ return eq(mapRej(f)(m))(bimap(f)(I)(m)); }); property('mapRej(f)(m) = swap(map(f)(swap(m)))', anyFuture, function (m){ return eq(mapRej(f)(m))(swap(map(f)(swap(m)))); }); property('map(f)(m) = swap(mapRej(f)(swap(m)))', anyFuture, function (m){ return eq(map(f)(m))(swap(mapRej(f)(swap(m)))); }); property('rejectAfter(1)(x) = reject(x)', any, function (x){ return eq(rejectAfter(1)(x))(reject(x)); }); ================================================ FILE: test/types/map.test-d.ts ================================================ import {expectType} from 'tsd'; import * as fl from '../../index.js'; const resolved = fl.resolve (42); const rejected = fl.reject ('uh-oh'); const resolvedPar = fl.Par (resolved); const rejectedPar = fl.Par (rejected); // Standard usage on Future instances. expectType> (fl.map (String) (resolved)); expectType> (fl.map (String) (rejected)); // Standard usage on ConcurrentFuture instances. expectType> (fl.map (String) (resolvedPar)); expectType> (fl.map (String) (rejectedPar)); // Usage with pipe on Future instances (https://git.io/JLx3F). expectType> (resolved .pipe (fl.map (String))); expectType> (rejected .pipe (fl.map (String))); // Function parameter inference from the second argument in a pipe (https://git.io/JLxsX). expectType> (resolved .pipe (fl.map (x => x))); expectType> (rejected .pipe (fl.map (x => x))); ================================================ FILE: test/unit/0.debug.js ================================================ import {eq, error, assertStackTrace, K, I, test} from '../util/util.js'; import { debugMode, debug, captureContext, captureApplicationContext, captureStackTraceFallback, } from '../../src/internal/debug.js'; import {nil} from '../../src/internal/list.js'; test('debug does nothing but return its first argument by default', function (done){ debugMode(false); eq(debug(null, done, error), null); eq(debug(42, done, error), 42); done(); }); test('debug runs the given function when debugMode was enabled', function (){ var guard = {}; debugMode(true); eq(debug(null, K(guard)), guard); eq(debug(null, I, guard), guard); }); test('captureContext returns previous when debugMode is off', function (){ debugMode(false); var previous = {}; var x = captureContext(previous, 2, 3); eq(x, previous); }); test('captureContext returns a list with a context object', function (){ debugMode(true); var prev = nil; var tag = 'hello world'; var expectedName = ' from ' + tag + ':'; var ctx = captureContext(prev, tag); eq(typeof ctx, 'object'); eq(ctx.tail, prev); eq(typeof ctx.head, 'object'); eq(ctx.head.tag, tag); eq(ctx.head.name, expectedName); assertStackTrace(expectedName, ctx.head.stack); }); test('captureApplicationContext returns previous when debugMode is off', function (){ debugMode(false); var previous = {}; var x = captureApplicationContext(previous, 1, Math.sqrt); eq(x, previous); }); test('captureApplicationContext returns a list with a context object', function (){ debugMode(true); var prev = nil; var expectedName = ' from first application of sqrt:'; var ctx = captureApplicationContext(prev, 1, Math.sqrt); eq(typeof ctx, 'object'); eq(ctx.tail, prev); eq(typeof ctx.head, 'object'); eq(ctx.head.tag, 'first application of sqrt'); eq(ctx.head.name, expectedName); assertStackTrace(expectedName, ctx.head.stack); }); test('captureStackTraceFallback assigns a stack trace to the given object', function (){ debugMode(false); var o = {name: 'test'}; captureStackTraceFallback(o); assertStackTrace('test', o.stack); }); ================================================ FILE: test/unit/0.error.js ================================================ import {eq, assertStackTrace, error as mockError, noop, test} from '../util/util.js'; import {mock} from '../util/futures.js'; import {namespace, name, version} from '../../src/internal/const.js'; import {nil, cons, cat} from '../../src/internal/list.js'; import { error, typeError, invalidArgument, invalidContext, invalidArity, invalidFutureArgument, wrapException, contextToStackTrace, } from '../../src/internal/error.js'; function args (){ return arguments; } test('error constructs an Error', function (){ eq(error('hello'), new Error('hello')); }); test('typeError constructs a TypeError', function (){ eq(typeError('hello'), new TypeError('hello')); }); test('invalidArgument constructs a TypeError', function (){ eq(invalidArgument('Test', 1, 'foo', 'bar'), new TypeError( 'Test() expects its second argument to foo.\n Actual: "bar" :: String' )); }); test('invalidContext constructs a TypeError', function (){ eq(invalidContext('Test', 'foo'), new TypeError( 'Test() was invoked outside the context of a Future. You might want ' + 'to use a dispatcher instead\n Called on: "foo"' )); }); test('invalidArity constructs a TypeError', function (){ eq(invalidArity(noop, args('one', 2, 3, 4, 5, 6)), new TypeError( 'noop() expects to be called with a single argument per invocation\n' + ' Saw: 6 arguments\n' + ' First: "one" :: String\n' + ' Second: 2 :: Number\n' + ' Third: 3 :: Number\n' + ' Fourth: 4 :: Number\n' + ' Fifth: 5 :: Number\n' + ' Argument 6: 6 :: Number' )); }); var mockType = function (identifier){ return {'@@type': identifier, '@@show': function (){ return 'mockType("' + identifier + '")'; }}; }; test('invalidFutureArgument warns us when nothing seems wrong', function (){ var actual = invalidFutureArgument('Foo', 0, mockType(namespace + '/' + name + '@' + version)); eq(actual, new TypeError( 'Foo() expects its first argument to be a valid Future.\n' + 'Nothing seems wrong. Contact the Fluture maintainers.\n' + ' Actual: mockType("fluture/Future@5") :: Future' )); }); test('invalidFutureArgument warns us about Futures from other sources', function (){ var actual = invalidFutureArgument('Foo', 0, mockType('bobs-tinkershop/' + name + '@' + version)); eq(actual, new TypeError( 'Foo() expects its first argument to be a valid Future.\n' + 'The Future was not created by fluture. ' + 'Make sure you transform other Futures to fluture Futures. ' + 'Got a Future from bobs-tinkershop.\n' + ' See: https://github.com/fluture-js/Fluture#casting-futures\n' + ' Actual: mockType("bobs-tinkershop/Future@5") :: Future' )); }); test('invalidFutureArgument warns us about Futures from unnamed sources', function (){ var actual = invalidFutureArgument('Foo', 0, mockType(name)); eq(actual, new TypeError( 'Foo() expects its first argument to be a valid Future.\n' + 'The Future was not created by fluture. ' + 'Make sure you transform other Futures to fluture Futures. ' + 'Got an unscoped Future.\n' + ' See: https://github.com/fluture-js/Fluture#casting-futures\n' + ' Actual: mockType("Future") :: Future' )); }); test('invalidFutureArgument warns about older versions', function (){ var actual = invalidFutureArgument('Foo', 0, mockType(namespace + '/' + name + '@' + (version - 1))); eq(actual, new TypeError( 'Foo() expects its first argument to be a valid Future.\n' + 'The Future was created by an older version of fluture. ' + 'This means that one of the sources which creates Futures is outdated. ' + 'Update this source, or transform its created Futures to be compatible.\n' + ' See: https://github.com/fluture-js/Fluture#casting-futures\n' + ' Actual: mockType("fluture/Future@4") :: Future' )); }); test('invalidFutureArgument warns about newer versions', function (){ var actual = invalidFutureArgument('Foo', 0, mockType(namespace + '/' + name + '@' + (version + 1))); eq(actual, new TypeError( 'Foo() expects its first argument to be a valid Future.\n' + 'The Future was created by a newer version of fluture. ' + 'This means that one of the sources which creates Futures is outdated. ' + 'Update this source, or transform its created Futures to be compatible.\n' + ' See: https://github.com/fluture-js/Fluture#casting-futures\n' + ' Actual: mockType("fluture/Future@6") :: Future' )); }); test('wrapException converts any value to an Error', function (){ var evilValue = {}; evilValue.__defineGetter__('name', () => { throw new Error }); evilValue.__defineGetter__('stack', () => { throw new Error }); eq(wrapException(new Error('test'), mock) instanceof Error, true); eq(wrapException(new TypeError('test'), mock) instanceof Error, true); eq(wrapException('test', mock) instanceof Error, true); eq(wrapException({foo: 'bar'}, mock) instanceof Error, true); eq(wrapException(evilValue, mock) instanceof Error, true); eq(wrapException(null, mock) instanceof Error, true); eq(wrapException({crash: null}, mock) instanceof Error, true); }); test('wrapException creates an error which encompasses the given error', function (){ var m = Object.create(mock); m.context = cons({stack: 'hello'}, cons({stack: 'world'}, nil)); var e = wrapException(mockError, m); eq(e.name, 'Error'); eq(e.message, mockError.message); eq(e.reason, mockError); eq(e.context, m.context); eq(e.future, m); assertStackTrace('Error: Intentional error for unit testing', e.stack); var m2 = Object.create(mock); m2.context = cons({stack: 'foo'}, cons({stack: 'bar'}, nil)); var e2 = wrapException(e, m2); eq(e2.name, 'Error'); eq(e2.message, mockError.message); eq(e2.reason, mockError); eq(e2.context, cat(m.context, m2.context)); eq(e2.future, m); assertStackTrace('Error: Intentional error for unit testing', e2.stack); }); test('contextToStackTrace converts a nested context structure to a stack trace', function (){ eq(contextToStackTrace(cons({stack: 'hello'}, cons({stack: 'world'}, nil))), '\nhello\nworld'); }); ================================================ FILE: test/unit/0.iteration.js ================================================ import chai from 'chai'; import {test} from '../util/util.js'; import {Next, Done, isIteration} from '../../src/internal/iteration.js'; var expect = chai.expect; test('Next', function (){ var actual = Next(42); expect(isIteration(actual)).to.equal(true); expect(actual.done).to.equal(false); expect(actual.value).to.equal(42); }); test('Done', function (){ var actual = Done(42); expect(isIteration(actual)).to.equal(true); expect(actual.done).to.equal(true); expect(actual.value).to.equal(42); }); ================================================ FILE: test/unit/0.list.js ================================================ import {eq, test} from '../util/util.js'; import {nil, cons, reverse, cat} from '../../src/internal/list.js'; test('reverse', function (){ eq(reverse(nil), nil); eq(reverse(cons('a', nil)), cons('a', nil)); eq(reverse(cons('a', cons('b', nil))), cons('b', cons('a', nil))); eq(reverse(cons('a', cons('b', cons('c', nil)))), cons('c', cons('b', cons('a', nil)))); }); test('cat', function (){ eq(cat(nil, nil), nil); eq(cat(cons('a', nil), nil), cons('a', nil)); eq(cat(nil, cons('a', nil)), cons('a', nil)); eq( cat(cons('a', cons('b', nil)), cons('c', cons('d', nil))), cons('a', cons('b', cons('c', cons('d', nil)))) ); }); ================================================ FILE: test/unit/0.predicates.js ================================================ import chai from 'chai'; import {test, eq, noop} from '../util/util.js'; import Future from '../../index.js'; import * as util from '../../src/internal/predicates.js'; var expect = chai.expect; test('isThenable', function (){ var ps = [ Promise.resolve(1), new Promise(noop), {then: noop}, {then (a){ return a }}, {then (a, b){ return b }}, ]; var values = [NaN, 1, true, undefined, null, [], {}]; var xs = values.concat([noop]).concat(values.map(function (x){ return ({then: x}) })); ps.forEach(function (p){ return expect(util.isThenable(p)).to.equal(true) }); xs.forEach(function (x){ return expect(util.isThenable(x)).to.equal(false) }); }); test('isFunction', function (){ var xs = [NaN, 1, true, undefined, null, [], {}]; eq(util.isFunction(function (){}), true); eq(util.isFunction(Future), true); xs.forEach(function (x){ return expect(util.isFunction(x)).to.equal(false) }); }); test('isUnsigned', function (){ var is = [1, 2, 999999999999999, Infinity]; var xs = [NaN, 0, -0, -1, -999999999999999, -Infinity, '1', [], {}]; is.forEach(function (i){ return expect(util.isUnsigned(i)).to.equal(true) }); xs.forEach(function (x){ return expect(util.isUnsigned(x)).to.equal(false) }); }); test('isObject', function (){ function O (){} var os = [{}, {foo: 1}, Object.create(null), new O, []]; var xs = [1, true, NaN, null, undefined, '']; os.forEach(function (i){ return expect(util.isObject(i)).to.equal(true) }); xs.forEach(function (x){ return expect(util.isObject(x)).to.equal(false) }); }); test('isIterator', function (){ var is = [{next (){}}, {next (x){ return x }}, (function*(){}())]; var xs = [1, true, NaN, null, undefined, '', {}, {next: 1}]; is.forEach(function (i){ return expect(util.isIterator(i)).to.equal(true) }); xs.forEach(function (x){ return expect(util.isIterator(x)).to.equal(false) }); }); ================================================ FILE: test/unit/0.utils.js ================================================ import chai from 'chai'; import {test} from '../util/util.js'; import {setImmediateFallback} from '../../src/internal/utils.js'; var expect = chai.expect; test('setImmediateFallback', function (done){ var t = setTimeout(done, 25, new Error('Time is up')); setImmediateFallback(function (x){ expect(x).to.equal(42); clearTimeout(t); done(); }, 42); }); ================================================ FILE: test/unit/1.future.js ================================================ import Future from '../../index.js'; import {noop, eq, assertCrashed, assertResolved, error, assertValidFuture, test} from '../util/util.js'; import {testFunction, functionArg} from '../util/props.js'; testFunction('Future', Future, [functionArg], assertValidFuture); test('crashes when the computation throws an error', function (){ var m = Future(function (){ throw error }); return assertCrashed(m, error); }); test('crashes when the computation returns nonsense', function (){ var m = Future(function (){ return 1 }); return assertCrashed(m, new TypeError( 'The computation was expected to return a nullary cancellation function\n' + ' Actual: 1' )); }); test('does not crash when the computation returns a nullary function', function (){ return new Promise((res, rej) => { var m = Future(function (){ return function (){} }); m._interpret(rej, noop, noop); setTimeout(res, 20); }); }); test('settles using the last synchronously called continuation', function (){ var actual = Future(function (rej, res){ res(1); rej(2); res(3); return noop; }); return assertResolved(actual, 3); }); test('settles using the first asynchronously called continuation', function (){ var actual = Future(function (rej, res){ setTimeout(res, 10, 1); setTimeout(res, 50, 2); return noop; }); return assertResolved(actual, 1); }); test('stops continuations from being called after cancellation', function (){ return new Promise((res, rej) => { const fail = () => rej(error); Future(function (rej, res){ setTimeout(res, 20, 1); setTimeout(rej, 20, 1); return noop; }) ._interpret(rej, fail, fail)(); setTimeout(res, 25); }); }); test('cannot continue during cancellation (#216)', function (){ return new Promise((res, rej) => { const fail = () => rej(error); Future(function (rej, res){ return function (){ rej(); res(); }; }) ._interpret(rej, fail, fail)(); setTimeout(res, 20); }); }); test('stops cancellation from being called after continuations', function (){ return new Promise((res, rej) => { var m = Future(function (rej, res){ res(1); return function (){ rej(error) }; }); var cancel = m._interpret(rej, noop, noop); cancel(); res(); }); }); test('can be cast to String', function (){ var m = Future(function (rej, res){ res() }); eq(m.toString(), 'Future (function (rej, res){ res() })'); }); ================================================ FILE: test/unit/1.is-future.js ================================================ import {isFuture} from '../../index.js'; import {property, anyFuture, anyNonFuture} from '../util/props.js'; property('returns true about Futures', anyFuture, function (value){ return isFuture(value) === true; }); property('returns false about everything else', anyNonFuture, function (value){ return isFuture(value) === false; }); ================================================ FILE: test/unit/1.is-never.js ================================================ import {isNever, never} from '../../index.js'; import {any, property} from '../util/props.js'; import {eq, test} from '../util/util.js'; test('returns true about never', function (){ eq(isNever(never), true); }); property('returns false about everything else', any, function (value){ return isNever(value) === false; }); ================================================ FILE: test/unit/1.never.js ================================================ import {never} from '../../index.js'; import {assertValidFuture, noop, eq, test} from '../util/util.js'; test('is a Future', function (){ assertValidFuture(never); }); test('does nothing and returns a noop cancel function', function (){ var m = never; var cancel = m._interpret(noop, noop, noop); cancel(); }); test('returns the code to create the Never', function (){ var m = never; eq(m.toString(), 'never'); }); ================================================ FILE: test/unit/1.pipe.js ================================================ import {mock} from '../util/futures.js'; import {eq, throws, test} from '../util/util.js'; test('throws when not given a function', function (){ throws(function (){ mock.pipe(42); }, new TypeError( 'Future#pipe() expects its first argument to be a Function.\n' + ' Actual: 42 :: Number' )); }); test('transforms the Future using the given function', function (){ eq(mock.pipe(String), '(util.mock)'); }); ================================================ FILE: test/unit/2.done.js ================================================ import {done} from '../../index.js'; import {testFunction, functionArg, resolvedFutureArg} from '../util/props.js'; import {eq, isFunction, test} from '../util/util.js'; import {rejected, resolved} from '../util/futures.js'; testFunction('done', done, [functionArg, resolvedFutureArg], isFunction); test('passes the rejection value as first parameter', function (testDone){ done(function (x, y){ eq(x, 'rejected'); eq(y, undefined); testDone(); })(rejected); }); test('passes the resolution value as second parameter', function (testDone){ done(function (x, y){ eq(x, null); eq(y, 'resolved'); testDone(); })(resolved); }); ================================================ FILE: test/unit/2.extract-left.js ================================================ import {extractLeft} from '../../index.js'; import {testFunction, futureArg} from '../util/props.js'; import {isArray, test} from '../util/util.js'; import {mock} from '../util/futures.js'; testFunction('extractLeft', extractLeft, [futureArg], isArray); test('dispatches to #extractLeft', function (done){ var m = Object.create(mock); m.extractLeft = done; extractLeft(m); }); ================================================ FILE: test/unit/2.extract-right.js ================================================ import {extractRight} from '../../index.js'; import {testFunction, futureArg} from '../util/props.js'; import {isArray, test} from '../util/util.js'; import {mock} from '../util/futures.js'; testFunction('extractRight', extractRight, [futureArg], isArray); test('dispatches to #extractRight', function (done){ var m = Object.create(mock); m.extractRight = done; extractRight(m); }); ================================================ FILE: test/unit/2.fork-catch.js ================================================ import {forkCatch} from '../../index.js'; import {testFunction, functionArg, futureArg} from '../util/props.js'; import {eq, isFunction, error, noop, test} from '../util/util.js'; import {crashed, rejected, resolved} from '../util/futures.js'; testFunction('forkCatch', forkCatch, [functionArg, functionArg, functionArg, futureArg], isFunction); test('calls the first continuation with the crash exception', function (done){ function assertCrash (x){ eq(x, error); done(); } forkCatch(assertCrash)(noop)(noop)(crashed); }); test('calls the second continuation with the rejection reason', function (done){ function assertRejection (x){ eq(x, 'rejected'); done(); } forkCatch(noop)(assertRejection)(noop)(rejected); }); test('calls the third continuation with the resolution value', function (done){ function assertResolution (x){ eq(x, 'resolved'); done(); } forkCatch(noop)(noop)(assertResolution)(resolved); }); ================================================ FILE: test/unit/2.fork.js ================================================ import {fork} from '../../index.js'; import {testFunction, functionArg, futureArg} from '../util/props.js'; import {eq, isFunction, error, noop, itRaises, test} from '../util/util.js'; import {crashed, rejected, resolved} from '../util/futures.js'; testFunction('fork', fork, [functionArg, functionArg, futureArg], isFunction); itRaises('the crash exception', function (){ fork(noop)(noop)(crashed); }, error); test('calls the first continuation with the rejection reason', function (done){ function assertRejection (x){ eq(x, 'rejected'); done(); } fork(assertRejection)(noop)(rejected); }); test('calls the second continuation with the resolution value', function (done){ function assertResolution (x){ eq(x, 'resolved'); done(); } fork(noop)(assertResolution)(resolved); }); ================================================ FILE: test/unit/2.promise.js ================================================ import {promise} from '../../index.js'; import {testFunction, futureArg} from '../util/props.js'; import {noop, isThenable, eq, error, test} from '../util/util.js'; import {crashed, rejected, resolved} from '../util/futures.js'; process.addListener('unhandledRejection', noop); testFunction('promise', promise, [futureArg], isThenable); test('returns a Promise', function (){ var actual = promise(resolved); eq(actual instanceof Promise, true); }); test('rejects if the Future crashes', function (){ return promise(crashed).then( function (){ throw new Error('It resolved') }, function (x){ eq(x, error) } ); }); test('rejects if the Future rejects', function (){ return promise(rejected).then( function (){ throw new Error('It resolved') }, function (x){ eq(x, 'rejected') } ); }); test('resolves if the Future resolves', function (){ return promise(resolved).then( function (x){ eq(x, 'resolved') } ); }); ================================================ FILE: test/unit/2.value.js ================================================ import show from 'sanctuary-show'; import {value} from '../../index.js'; import {testFunction, functionArg, resolvedFutureArg} from '../util/props.js'; import {eq, isFunction, noop, itRaises, error, test} from '../util/util.js'; import {crashed, rejected, resolved} from '../util/futures.js'; testFunction('value', value, [functionArg, resolvedFutureArg], isFunction); itRaises('when the Future crashes', function (){ value(noop)(rejected); }, error); itRaises('when the Future rejects', function (){ value(noop)(crashed); }, new Error( 'Future#value was called on a rejected Future\n' + ' Rejection: "rejected"\n' + ' Future: ' + show(rejected) )); test('calls the continuation with the resolution value', function (done){ value(function (x){ eq(x, 'resolved'); done(); })(resolved); }); ================================================ FILE: test/unit/3.after.js ================================================ import {after, never} from '../../index.js'; import {eq, assertValidFuture, assertResolved, test, error} from '../util/util.js'; import {testFunction, positiveIntegerArg, anyArg} from '../util/props.js'; testFunction('after', after, [positiveIntegerArg, anyArg], assertValidFuture); test('returns Never when given Infinity', function (){ eq(after(Infinity)(1), never); }); test('resolves with the given value', function (){ return assertResolved(after(20)(1), 1); }); test('clears its internal timeout when cancelled', function (done){ const fail = () => done(error); after(20)(1)._interpret(done, fail, fail)(); setTimeout(done, 25); }); test('returns array with the value', function (){ eq(after(20)(1).extractRight(), [1]); }); test('returns the code to create the After when cast to String', function (){ eq(after(20)(1).toString(), 'after (20) (1)'); }); ================================================ FILE: test/unit/3.attempt-p.js ================================================ /* eslint prefer-promise-reject-errors: 0 */ import chai from 'chai'; import {attemptP, map, mapRej} from '../../index.js'; import {assertCrashed, assertRejected, assertResolved, assertValidFuture, error, noop, test} from '../util/util.js'; import {testFunction, functionArg} from '../util/props.js'; var expect = chai.expect; testFunction('encaseP', attemptP, [functionArg], assertValidFuture); test('crashes when the Promise generator throws', function (){ var m = attemptP(function (){ throw error }); return assertCrashed(m, error); }); test('crashes when the Promise generator does not return a Promise', function (){ var m = attemptP(noop); return assertCrashed(m, new TypeError( 'encaseP() expects the function it\'s given to return a Promise/Thenable\n' + ' Actual: undefined\n' + ' From calling: function (){}\n' + ' With: undefined' )); }); test('resolves with the resolution value of the returned Promise', function (){ var actual = attemptP(function (){ return Promise.resolve(1) }); return assertResolved(actual, 1); }); test('rejects with rejection reason of the returned Promise', function (){ var actual = attemptP(function (){ return Promise.reject(error) }); return assertRejected(actual, error); }); test('ensures no resolution happens after cancel', function (done){ const fail = () => done(error); var actual = attemptP(function (){ return Promise.resolve(1) }); actual._interpret(done, fail, fail)(); setTimeout(done, 20); }); test('ensures no rejection happens after cancel', function (done){ const fail = () => done(error); var actual = attemptP(function (){ return Promise.reject(1) }); actual._interpret(done, fail, fail)(); setTimeout(done, 20); }); test('crashes with errors that occur in rejection continuation', function (){ var m = map(function (){ throw error })(attemptP(function (){ return Promise.resolve(1) })); return assertCrashed(m, error); }); test('crashes with errors that occur in resolution continuation', function (){ var m = mapRej(function (){ throw error })(attemptP(function (){ return Promise.reject(1) })); return assertCrashed(m, error); }); test('returns the code to create the Future when cast to String', function (){ var f = function (){ return Promise.resolve(42) }; var m = attemptP(f); expect(m.toString()).to.equal('encaseP (' + f.toString() + ') (undefined)'); }); ================================================ FILE: test/unit/3.attempt.js ================================================ import chai from 'chai'; import {attempt, map} from '../../index.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, error} from '../util/util.js'; import {testFunction, functionArg} from '../util/props.js'; var expect = chai.expect; testFunction('encase', attempt, [functionArg], assertValidFuture); test('resolves with the return value of the function', function (){ var actual = attempt(function (){ return 1 }); return assertResolved(actual, 1); }); test('rejects with the exception thrown by the function', function (){ var actual = attempt(function (){ throw error }); return assertRejected(actual, error); }); test('does not swallow errors from subsequent maps and such', function (){ var m = map(function (){ throw error })(attempt(function (x){ return x })); return assertCrashed(m, error); }); test('returns the code to create the Future', function (){ var f = function (){}; var m = attempt(f); expect(m.toString()).to.equal('encase (' + f.toString() + ') (undefined)'); }); ================================================ FILE: test/unit/3.cache.js ================================================ import chai from 'chai'; import {Future, cache, resolve, reject, after, rejectAfter} from '../../index.js'; import {Crashed, Rejected, Resolved} from '../../src/cache.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, error, noop, onceOrError} from '../util/util.js'; import * as F from '../util/futures.js'; import {testFunction, futureArg} from '../util/props.js'; var expect = chai.expect; testFunction('cache', cache, [futureArg], assertValidFuture); test('interpret crashes if the underlying computation crashes', function (){ return assertCrashed(cache(F.crashed), error); }); test('interpret resolves with the resolution value resolve the given Future', function (){ return assertResolved(cache(resolve(1)), 1); }); test('interpret rejects with the rejection reason resolve the given Future', function (){ return assertRejected(cache(reject(error)), error); }); test('interpret only interprets its given Future once', function (){ var m = cache(Future(onceOrError(function (rej, res){ res(1); return noop }))); m._interpret(noop, noop, noop); m._interpret(noop, noop, noop); return assertResolved(m, 1); }); test('interpret crashes all consumers once a delayed crash happens', function (){ var m = cache(F.crashedSlow); var a = assertCrashed(m, error); var b = assertCrashed(m, error); var c = assertCrashed(m, error); return Promise.all([a, b, c]); }); test('interpret resolves all consumers once a delayed resolution happens', function (){ var m = cache(after(200)(1)); var a = assertResolved(m, 1); var b = assertResolved(m, 1); var c = assertResolved(m, 1); return Promise.all([a, b, c]); }); test('interpret rejects all consumers once a delayed rejection happens', function (){ var m = cache(rejectAfter(20)(error)); var a = assertRejected(m, error); var b = assertRejected(m, error); var c = assertRejected(m, error); return Promise.all([a, b, c]); }); test('interpret crashes all new consumers after a crash happened', function (){ var m = cache(F.crashed); m._interpret(noop, noop, noop); return assertCrashed(m, error); }); test('interpret rejects all new consumers after a rejection happened', function (){ var m = cache(reject('err')); m._interpret(noop, noop, noop); return assertRejected(m, 'err'); }); test('interpret it iinterpret nterprets the internal Future again when interpreted after having been cancelled', function (done){ var m = cache(Future(function (rej, res){ var o = {cancelled: false}; var id = setTimeout(res, 20, o); return function (){ o.cancelled = true; clearTimeout(id) }; })); var clear = m._interpret(done, noop, noop); setTimeout(function (){ clear(); m._interpret(done, noop, function (v){ return (expect(v).to.have.property('cancelled', false), done()) }); }, 10); }); test('interpret does not reset when one resolve multiple listeners is cancelled', function (done){ var m = cache(Future(function (rej, res){ setTimeout(res, 5, 1); return function (){ return done(new Error('Reset happened')) }; })); var cancel = m._interpret(done, noop, noop); m._interpret(done, noop, noop); cancel(); setTimeout(done, 20); }); test('interpret does not change when cancelled after settled', function (done){ var m = cache(Future(function (rej, res){ res(1); return function (){ return done(new Error('Cancelled after settled')) }; })); var cancel = m._interpret(done, noop, noop); setTimeout(function (){ cancel(); done(); }, 5); }); test('crash sets state to Crashed', function (){ var m = cache(Future(noop)); m.crash(1); expect(m._state).to.equal(Crashed); }); test('crash does nothing when state is resolved', function (){ var m = cache(Future(noop)); m.resolve(1); m.crash(2); expect(m._state).to.equal(Resolved); }); test('resolve does nothing when state is rejected', function (){ var m = cache(Future(noop)); m.reject(1); m.resolve(2); expect(m._state).to.equal(Rejected); }); test('reject does nothing when state is resolved', function (){ var m = cache(Future(noop)); m.resolve(1); m.reject(2); expect(m._state).to.equal(Resolved); }); test('_addToQueue does nothing when state is settled', function (){ var m = cache(Future(noop)); m.resolve(1); m._addToQueue(noop, noop); expect(m._queued).to.equal(0); }); test('_drainQueue is idempotent', function (){ var m = cache(resolve(1)); m._drainQueue(); m._drainQueue(); m._interpret(noop, noop, noop); m._drainQueue(); m._drainQueue(); }); test('run is idempotent', function (){ var m = cache(resolve(1)); m.run(); m.run(); }); test('reset is idempotent', function (){ var m = cache(resolve(1)); m.reset(); m._interpret(noop, noop, noop); m.reset(); m.reset(); }); test('reset cancels the underlying computation', function (done){ var m = cache(Future(function (){ return function (){ done() } })); m.run(); m.reset(); }); test('returns the code to create the Cache when cast to String', function (){ var m = cache(resolve(1)); var s = 'cache (resolve (1))'; expect(m.toString()).to.equal(s); }); test('extractLeft returns empty array for cold Cacheds', function (){ expect(cache(reject(1)).extractLeft()).to.deep.equal([]); }); test('extractLeft returns array with reason for rejected Cacheds', function (){ var m = cache(reject(1)); m.run(); expect(m.extractLeft()).to.deep.equal([1]); }); test('extractRight returns empty array for cold Cacheds', function (){ expect(cache(resolve(1)).extractRight()).to.deep.equal([]); }); test('extractRight returns array with value for resolved Cacheds', function (){ var m = cache(resolve(1)); m.run(); expect(m.extractRight()).to.deep.equal([1]); }); ================================================ FILE: test/unit/3.crash.js ================================================ import {crash} from '../../src/future.js'; import {eq, assertIsFuture, assertCrashed, test} from '../util/util.js'; import {testFunction, anyArg} from '../util/props.js'; testFunction('crash', crash, [anyArg], assertIsFuture); test('returns a crashed Future', function (){ return assertCrashed(crash(1), 1); }); test('can be shown as string', function (){ eq(crash(1).toString(), 'crash (1)'); }); ================================================ FILE: test/unit/3.encase-p.js ================================================ /* eslint prefer-promise-reject-errors: 0 */ import chai from 'chai'; import {encaseP} from '../../index.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, error, noop} from '../util/util.js'; import {testFunction, functionArg, anyArg} from '../util/props.js'; var expect = chai.expect; testFunction('encaseP', encaseP, [functionArg, anyArg], assertValidFuture); test('crashes when the Promise generator throws', function (){ var m = encaseP(function (){ throw error })(1); return assertCrashed(m, error); }); test('crashes when the Promise generator does not return a Promise', function (){ var m = encaseP(noop)(1); return assertCrashed(m, new TypeError( 'encaseP() expects the function it\'s given to return a Promise/Thenable\n' + ' Actual: undefined\n' + ' From calling: function (){}\n' + ' With: 1' )); }); test('resolves with the resolution value of the returned Promise', function (){ var actual = encaseP(function (x){ return Promise.resolve(x + 1) })(1); return assertResolved(actual, 2); }); test('rejects with rejection reason of the returned Promise', function (){ var actual = encaseP(function (){ return Promise.reject(error) })(1); return assertRejected(actual, error); }); test('ensures no resolution happens after cancel', function (done){ const fail = () => done(error); var actual = encaseP(function (x){ return Promise.resolve(x + 1) })(1); actual._interpret(done, fail, fail)(); setTimeout(done, 20); }); test('ensures no rejection happens after cancel', function (done){ const fail = () => done(error); var actual = encaseP(function (x){ return Promise.reject(x + 1) })(1); actual._interpret(done, fail, fail)(); setTimeout(done, 20); }); test('returns the code to create the Future when cast to String', function (){ var f = function (a){ return Promise.resolve(a) }; var m = encaseP(f)(null); expect(m.toString()).to.equal('encaseP (' + f.toString() + ') (null)'); }); ================================================ FILE: test/unit/3.encase.js ================================================ import chai from 'chai'; import {encase, map} from '../../index.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, error} from '../util/util.js'; import {testFunction, functionArg, anyArg} from '../util/props.js'; var expect = chai.expect; testFunction('encase', encase, [functionArg, anyArg], assertValidFuture); test('resolves with the return value of the function', function (){ var actual = encase(function (x){ return x + 1 })(1); return assertResolved(actual, 2); }); test('rejects with the exception thrown by the function', function (){ var actual = encase(function (a){ throw a, error })(1); return assertRejected(actual, error); }); test('does not swallow errors from subsequent maps and such', function (){ var m = map(function (){ throw error })(encase(function (x){ return x })(1)); return assertCrashed(m, error); }); test('returns the code to create the Future when cast to String', function (){ var f = function (a){ return void a }; var m = encase(f)(null); expect(m.toString()).to.equal('encase (' + f.toString() + ') (null)'); }); ================================================ FILE: test/unit/3.go.js ================================================ import chai from 'chai'; import {Future, go, resolve, after} from '../../index.js'; import {test, assertCrashed, assertResolved, assertValidFuture, error, noop, STACKSIZE} from '../util/util.js'; import * as F from '../util/futures.js'; import {testFunction, functionArg} from '../util/props.js'; var expect = chai.expect; testFunction('go', go, [functionArg], assertValidFuture); test('crashes when the given function throws an error', function (){ var m = go(function (){ throw error }); return assertCrashed(m, error); }); test('crashes when the given function does not return an interator', function (){ var m = go(function (){ return null }); return assertCrashed(m, new TypeError( 'go() expects its first argument to return an iterator, maybe you forgot the "*".\n' + ' Actual: null :: Null' )); }); test('crashes when iterator.next() throws an error', function (){ var m = go(function (){ return {next: () => { throw error }} }); return assertCrashed(m, error); }); test('crashes when the returned iterator does not return a valid iteration', function (){ var m = go(function (){ return {next: () => { return null }} }); return assertCrashed(m, new TypeError( 'The iterator did not return a valid iteration from iterator.next()\n' + ' Actual: null' )); }); test('crashes when the returned iterator produces something other than a Future', function (){ var m = go(function (){ return {next: () => { return {done: false, value: null} }} }); return assertCrashed(m, new TypeError( 'go() expects the value produced by the iterator to be a valid Future.\n' + ' Actual: null :: Null\n' + ' Tip: If you\'re using a generator, make sure you always yield a Future' )); }); test('crashes when the yielded Future crashes', function (){ var m = go(function*(){ yield F.crashed }); return assertCrashed(m, error); }); test('handles synchronous Futures', function (){ return assertResolved(go(function*(){ var a = yield resolve(1); var b = yield resolve(2); return a + b; }), 3); }); test('handles asynchronous Futures', function (){ return assertResolved(go(function*(){ var a = yield after(10)(1); var b = yield after(10)(2); return a + b; }), 3); }); test('does not mix state over multiple interpretations', function (){ var m = go(function*(){ var a = yield resolve(1); var b = yield after(10)(2); return a + b; }); return Promise.all([ assertResolved(m, 3), assertResolved(m, 3), ]); }); test('is stack safe', function (){ var gen = function*(){ var i = 0; while(i < STACKSIZE + 1){ yield resolve(i++) } return i; }; var m = go(gen); return assertResolved(m, STACKSIZE + 1); }); test('cancels the running operation when cancelled', function (done){ var cancel = go(function*(){ yield resolve(1); yield Future(function (){ return function (){ return done() } }); })._interpret(done, noop, noop); cancel(); }); test('returns the code to create the Go when cast to String', function (){ var f = function*(){}; var m = go(f); var s = 'go (' + f.toString() + ')'; expect(m.toString()).to.equal(s); }); ================================================ FILE: test/unit/3.hook.js ================================================ import chai from 'chai'; import {Future, hook, resolve, reject} from '../../index.js'; import {test, assertCrashed, assertIsFuture, assertRejected, assertResolved, error, itRaises, K, noop} from '../util/util.js'; import * as F from '../util/futures.js'; import {testFunction, futureArg, functionArg} from '../util/props.js'; var expect = chai.expect; testFunction('hook', hook, [futureArg, functionArg, functionArg], assertIsFuture); test('crashes when the disposal function does not return Future', function (){ var m = hook(F.resolved)(function (){ return 1 })(function (){ return F.resolved }); return assertCrashed(m, new TypeError( 'hook() expects the return value from the first function it\'s given to be a valid Future.\n' + ' Actual: 1 :: Number\n' + ' From calling: function (){ return 1 }\n' + ' With: "resolved"' )); }); test('crashes when the disposal function throws', function (){ var m = hook(F.resolved)(function (){ throw error })(function (){ return F.resolved }); return assertCrashed(m, error); }); test('crashes when the computation function does not return Future', function (){ var m = hook(F.resolved)(function (){ return F.resolved })(function (){ return 1 }); return assertCrashed(m, new TypeError( 'hook() expects the return value from the second function it\'s given to be a valid Future.\n' + ' Actual: 1 :: Number\n' + ' From calling: function (){ return 1 }\n' + ' With: "resolved"' )); }); test('crashes when the computation function throws', function (){ var m = hook(F.resolved)(function (){ return F.resolved })(function (){ throw error }); return assertCrashed(m, error); }); test('crashes when the disposal Future rejects', function (){ var rejected = hook(F.resolved)(function (){ return reject(1) })(function (){ return reject(2) }); var resolved = hook(F.resolved)(function (){ return reject(1) })(function (){ return resolve(2) }); return Promise.all([ assertCrashed(rejected, new Error('The disposal Future rejected with 1')), assertCrashed(resolved, new Error('The disposal Future rejected with 1')), ]); }); test('runs the first computation after the second, both with the resource', function (done){ var ran = false; hook(F.resolved)(function (x){ expect(x).to.equal('resolved'); return Future(function (rej, res){ res(done(ran ? null : new Error('Second did not run'))); return noop }); })(function (x){ expect(x).to.equal('resolved'); return Future(function (rej, res){ res(ran = true); return noop }); })._interpret(done, done, noop); }); test('runs the first even if the second rejects', function (done){ hook(F.resolved)(function (){ return Future(function (){ done(); return noop }); })(function (){ return reject(2); })._interpret(done, noop, noop); }); test('assumes the state resolve the second if the first resolves', function (){ var rejected = hook(F.resolved)(function (){ return resolve(1) })(function (){ return reject(2) }); var resolved = hook(F.resolved)(function (){ return resolve(1) })(function (){ return resolve(2) }); return Promise.all([ assertRejected(rejected, 2), assertResolved(resolved, 2), ]); }); test('does not hook after being cancelled', function (done){ const fail = () => done(error); hook(F.resolvedSlow)(function (){ return resolve('dispose') })(fail)._interpret(done, fail, fail)(); setTimeout(done, 25); }); test('does not reject after being cancelled', function (done){ const fail = () => done(error); hook(F.rejectedSlow)(function (){ return resolve('dispose') })(fail)._interpret(done, fail, fail)(); hook(F.resolved)(function (){ return resolve('dispose') })(function (){ return F.rejectedSlow })._interpret(done, fail, fail)(); setTimeout(done, 25); }); test('cancels acquire appropriately', function (done){ const fail = () => done(error); var acquire = Future(function (){ return function (){ return done() } }); var cancel = hook(acquire)(function (){ return resolve('dispose') })(function (){ return resolve('consume') }) ._interpret(done, fail, fail); setTimeout(cancel, 10); }); test('cancels consume appropriately', function (done){ const fail = () => done(error); var consume = Future(function (){ return function (){ return done() } }); var cancel = hook(F.resolved)(function (){ return resolve('dispose') })(function (){ return consume }) ._interpret(done, fail, fail); setTimeout(cancel, 10); }); test('cancels delayed consume appropriately', function (done){ const fail = () => done(error); var consume = Future(function (){ return function (){ return done() } }); var cancel = hook(F.resolvedSlow)(function (){ return resolve('dispose') })(function (){ return consume }) ._interpret(done, fail, fail); setTimeout(cancel, 25); }); test('does not cancel disposal', function (done){ const fail = () => done(error); var dispose = Future(function (){ return function (){ return done(error) } }); var cancel = hook(F.resolved)(function (){ return dispose })(function (){ return resolve('consume') }) ._interpret(done, fail, fail); setTimeout(cancel, 10); setTimeout(done, 50); }); test('does not cancel delayed dispose', function (done){ const fail = () => done(error); var dispose = Future(function (){ return function (){ return done(error) } }); var cancel = hook(F.resolved)(function (){ return dispose })(function (){ return F.resolvedSlow }) ._interpret(done, fail, fail); setTimeout(cancel, 50); setTimeout(done, 100); }); test('runs the disposal Future when cancelled after acquire', function (done){ const fail = () => done(error); var cancel = hook(F.resolved)(function (){ return Future(function (){ done(); return noop }) })(function (){ return F.resolvedSlow }) ._interpret(done, fail, fail); setTimeout(cancel, 10); }); itRaises('exceptions that occur after the Future was unsubscribed', function (done){ const fail = () => done(error); hook(F.resolved)(K(F.crashedSlow))(K(F.resolved))._interpret(function (){ done(new Error('Exception handler called')); }, fail, fail)(); }, error); test('returns the code which creates the same data-structure when cast to String', function (){ var a = resolve(1); var d = function (){ return resolve(2) }; var c = function (){ return resolve(3) }; var m = hook(a)(d)(c); var expected = 'hook (' + a.toString() + ') (' + d.toString() + ') (' + c.toString() + ')'; expect(m.toString()).to.equal(expected); }); ================================================ FILE: test/unit/3.node.js ================================================ import chai from 'chai'; import {node} from '../../index.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, error} from '../util/util.js'; import {testFunction, functionArg} from '../util/props.js'; var expect = chai.expect; testFunction('node', node, [functionArg], assertValidFuture); test('crashes when the function throws', function (){ var m = node(function (){ throw error }); return assertCrashed(m, error); }); test('rejects when the callback is called with (err)', function (){ var f = function (done){ return done(error) }; return assertRejected(node(f), error); }); test('resolves when the callback is called with (null, a)', function (){ var f = function (done){ return done(null, 'a') }; return assertResolved(node(f), 'a'); }); test('settles with the last synchronous call to done', function (){ var f = function (done){ done(null, 'a'); done(error); done(null, 'b') }; return assertResolved(node(f), 'b'); }); test('settles with the first asynchronous call to done', function (){ var f = function (done){ setTimeout(done, 10, null, 'a'); setTimeout(done, 50, null, 'b'); }; return assertResolved(node(f), 'a'); }); test('ensures no continuations are called after cancel', function (done){ const fail = () => done(error); var f = function (done){ return setTimeout(done, 5) }; node(f)._interpret(done, fail, fail)(); setTimeout(done, 20); }); test('returns the code to create the Future when cast to String', function (){ var f = function (a){ return void a }; var m = node(f); expect(m.toString()).to.equal('node (' + f.toString() + ')'); }); ================================================ FILE: test/unit/3.parallel.js ================================================ import chai from 'chai'; import {Future, parallel, resolve, reject, after} from '../../index.js'; import {test, promiseTimeout, assertCrashed, assertRejected, assertResolved, assertValidFuture, error, noop, repeat, STACKSIZE} from '../util/util.js'; import * as F from '../util/futures.js'; import {testFunction, positiveIntegerArg, futureArrayArg} from '../util/props.js'; var expect = chai.expect; testFunction('parallel', parallel, [positiveIntegerArg, futureArrayArg], assertValidFuture); test('crashes when one resolve the Futures crash', function (){ return assertCrashed(parallel(2)([F.resolved, F.crashed]), error); }); test('crashes when one resolve the Futures crash', function (){ return assertCrashed(parallel(2)([F.resolved, F.resolved, F.resolved, F.resolved, F.resolved, F.crashed]), error); }); test('throws when the Array contains something other than Futures', function (){ var xs = [NaN, {}, [], 1, 'a', new Date, undefined, null]; var fs = xs.map(function (x){ return function (){ return parallel(1)([x])._interpret(noop, noop, noop) } }); fs.forEach(function (f){ return expect(f).to.throw(TypeError, /Future/u) }); }); test('parallelizes execution', function (){ var actual = parallel(5)([ after(20)('a'), after(20)('b'), after(20)('c'), after(20)('d'), after(20)('e'), ]); return promiseTimeout(70, assertResolved(actual, ['a', 'b', 'c', 'd', 'e'])); }); test('limits parallelism to the given number', function (){ var running = 0; var m = Future(function (rej, res){ running++; if(running > 2){ return void rej(new Error('More than two running in parallel')) } setTimeout(function (){ running--; res('a'); }, 20); return noop; }); var actual = parallel(2)(repeat(8, m)); return assertResolved(actual, repeat(8, 'a')); }); test('runs all in parallel when given number larger than the array length', function (){ var actual = parallel(10)([ after(20)('a'), after(20)('b'), after(20)('c'), after(20)('d'), after(20)('e'), ]); return promiseTimeout(70, assertResolved(actual, ['a', 'b', 'c', 'd', 'e'])); }); test('can deal with synchronously resolving futures', function (){ return assertResolved(parallel(5)(repeat(10, resolve(1))), [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); }); test('interprets the synchronous futures in the provided sequence', function (done){ var ns = Array.from({length: 10}, function (_, i){ return i }); var xs = []; var ms = ns.map(function (i){ return Future(function (rej, res){ xs.push(i); res(i); return noop; }); }); parallel(5)(ms)._interpret(done, noop, function (out){ expect(out).to.deep.equal(ns); expect(xs).to.deep.equal(ns); done(); }); }); test('interprets the asynchronous futures in the provided sequence', function (done){ var ns = Array.from({length: 10}, function (_, i){ return i }); var xs = []; var ms = ns.map(function (i){ return Future(function (rej, res){ xs.push(i); setTimeout(res, 10, i); return noop; }); }); parallel(5)(ms)._interpret(done, noop, function (out){ expect(out).to.deep.equal(ns); expect(xs).to.deep.equal(ns); done(); }); }); test('resolves to an empty array when given an empty array', function (){ return assertResolved(parallel(1)([]), []); }); test('runs all in parallel when given Infinity', function (){ var actual = parallel(Infinity)([ after(20)('a'), after(20)('b'), after(20)('c'), after(20)('d'), after(20)('e'), ]); return promiseTimeout(70, assertResolved(actual, ['a', 'b', 'c', 'd', 'e'])); }); test('rejects if one resolve the input rejects', function (){ var actual = parallel(2)([F.resolved, reject('err')]); return assertRejected(actual, 'err'); }); test('rejects with the first rejection value', function (){ return Promise.all([ assertRejected(parallel(2)([F.rejectedSlow, F.rejected]), 'rejected'), assertRejected(parallel(2)([F.rejected, F.rejectedSlow]), 'rejected'), ]); }); test('cancels Futures when cancelled', function (done){ var m = Future(function (){ return function (){ return done() } }); var cancel = parallel(1)([m])._interpret(done, noop, noop); setTimeout(cancel, 20); }); test('cancels only running Futures when cancelled', function (done){ var i = 0, j = 0; var m = Future(function (rej, res){ var x = setTimeout(function (x){ j += 1; res(x) }, 20, 1); return function (){ i += 1; clearTimeout(x); }; }); var cancel = parallel(2)([m, m, m, m])._interpret(done, noop, noop); setTimeout(function (){ cancel(); expect(i).to.equal(2); expect(j).to.equal(2); done(); }, 30); }); test('does not interpret any computations after one rejects', function (done){ var m = Future(function (){ done(error) }); parallel(2)([F.rejected, m])._interpret(done, noop, noop); done(); }); test('automatically cancels running computations when one rejects', function (done){ var m = Future(function (){ return function (){ done() } }); parallel(2)([m, F.rejected])._interpret(done, noop, noop); }); test('does not cancel settled computations (#123)', function (done){ var m1 = Object.create(F.mock); var m2 = Object.create(F.mock); m1._interpret = function (_, rej, res){ setTimeout(res, 10, 1); return function (){ return done(error) }; }; m2._interpret = function (_, rej){ setTimeout(rej, 50, 2); return function (){ return done(error) }; }; parallel(2)([m1, m2])._interpret(done, noop, noop); setTimeout(done, 100, null); }); test('does not resolve after being cancelled', function (done){ const fail = () => done(error); const cancel = parallel(1)([F.resolvedSlow, F.resolvedSlow]) ._interpret(done, fail, fail); cancel(); setTimeout(done, 100); }); test('does not reject after being cancelled', function (done){ const fail = () => done(error); const cancel = parallel(1)([F.rejectedSlow, F.rejectedSlow]) ._interpret(done, fail, fail); cancel(); setTimeout(done, 100); }); test('is stack safe (#130)', function (){ var ms = Array.from({length: STACKSIZE}, (_, i) => resolve(i)); var expected = Array.from({length: STACKSIZE}, (_, i) => i); return assertResolved(parallel(1)(ms), expected); }); test('returns the code to create the Parallel when cast to String', function (){ var m1 = parallel(Infinity)([resolve(1), resolve(2)]); var m2 = parallel(2)([resolve(1), resolve(2)]); var s1 = 'parallel (Infinity) ([resolve (1), resolve (2)])'; var s2 = 'parallel (2) ([resolve (1), resolve (2)])'; expect(m1.toString()).to.equal(s1); expect(m2.toString()).to.equal(s2); }); ================================================ FILE: test/unit/3.reject-after.js ================================================ import {rejectAfter, never} from '../../index.js'; import {eq, assertValidFuture, assertRejected, test, error} from '../util/util.js'; import {testFunction, positiveIntegerArg, anyArg} from '../util/props.js'; testFunction('rejectAfter', rejectAfter, [positiveIntegerArg, anyArg], assertValidFuture); test('returns Never when given Infinity', function (){ eq(rejectAfter(Infinity)(1), never); }); test('rejects with the given value', function (){ return assertRejected(rejectAfter(20)(1), 1); }); test('clears its internal timeout when cancelled', function (done){ const fail = () => done(error); rejectAfter(20)(1)._interpret(done, fail, fail)(); setTimeout(done, 25); }); test('returns array with the value', function (){ eq(rejectAfter(20)(1).extractLeft(), [1]); }); test('returns the code to create the After when cast to String', function (){ eq(rejectAfter(20)(1).toString(), 'rejectAfter (20) (1)'); }); ================================================ FILE: test/unit/3.reject.js ================================================ import {reject} from '../../index.js'; import {eq, assertValidFuture, assertRejected, test} from '../util/util.js'; import {testFunction, anyArg} from '../util/props.js'; testFunction('reject', reject, [anyArg], assertValidFuture); test('returns a rejected Future', function (){ return assertRejected(reject(1), 1); }); test('provides its reason to extractLeft()', function (){ eq(reject(1).extractLeft(), [1]); }); test('can be shown as string', function (){ eq(reject(1).toString(), 'reject (1)'); }); ================================================ FILE: test/unit/3.resolve.js ================================================ import {resolve} from '../../index.js'; import {eq, assertValidFuture, assertResolved, test} from '../util/util.js'; import {testFunction, anyArg} from '../util/props.js'; testFunction('resolve', resolve, [anyArg], assertValidFuture); test('returns a resolved Future', function (){ return assertResolved(resolve(1), 1); }); test('provides its reason to extractRight()', function (){ eq(resolve(1).extractRight(), [1]); }); test('can be shown as string', function (){ eq(resolve(1).toString(), 'resolve (1)'); }); ================================================ FILE: test/unit/4.alt.js ================================================ import Either from 'sanctuary-either'; import {Future, alt} from '../../index.js'; import {assertCrashed, eq, assertValidFuture, noop, assertResolved, assertRejected, error, test} from '../util/util.js'; import {crashed, rejected, resolved, rejectedSlow, resolvedSlow} from '../util/futures.js'; import {testFunction, altArg, futureArg} from '../util/props.js'; testFunction('alt', alt, [altArg, futureArg], assertValidFuture); test('chooses the resolved over the rejected Future', function (){ return Promise.all([ assertResolved(alt(crashed)(resolved), 'resolved'), assertResolved(alt(resolved)(resolved), 'resolved'), assertResolved(alt(rejected)(resolved), 'resolved'), assertResolved(alt(resolved)(rejected), 'resolved'), assertRejected(alt(rejected)(rejected), 'rejected'), assertResolved(alt(resolved)(resolvedSlow), 'resolvedSlow'), assertResolved(alt(resolvedSlow)(resolved), 'resolved'), assertRejected(alt(rejected)(rejectedSlow), 'rejected'), assertRejected(alt(rejectedSlow)(rejected), 'rejectedSlow'), assertCrashed(alt(rejected)(crashed), error), assertCrashed(alt(resolved)(crashed), error), assertCrashed(alt(crashed)(rejected), error), ]); }); test('dispatches to Fantasy Land alt', function (){ eq(alt(Either.Right(42))(Either.Left(42)), Either.Right(42)); }); test('cancels the running Future', function (done){ var m = Future(function (){ return function (){ return done() } }); var cancel = alt(m)(m)._interpret(done, noop, noop); cancel(); }); test('displays correctly as string', function (){ eq(alt(rejected)(resolved).toString(), 'alt (reject ("rejected")) (resolve ("resolved"))'); }); ================================================ FILE: test/unit/4.and.js ================================================ import {Future, and} from '../../index.js'; import {assertCrashed, eq, assertValidFuture, noop, assertResolved, assertRejected, error, test} from '../util/util.js'; import {crashed, rejected, resolved, rejectedSlow, resolvedSlow} from '../util/futures.js'; import {testFunction, futureArg} from '../util/props.js'; testFunction('and', and, [futureArg, futureArg], assertValidFuture); test('chooses the rejected over the resolved Future', function (){ return Promise.all([ assertResolved(and(resolved)(resolved), 'resolved'), assertRejected(and(rejected)(resolved), 'rejected'), assertRejected(and(resolved)(rejected), 'rejected'), assertRejected(and(rejected)(rejected), 'rejected'), assertResolved(and(resolved)(resolvedSlow), 'resolved'), assertResolved(and(resolvedSlow)(resolved), 'resolvedSlow'), assertRejected(and(rejected)(rejectedSlow), 'rejectedSlow'), assertRejected(and(rejectedSlow)(rejected), 'rejected'), assertCrashed(and(resolved)(crashed), error), assertCrashed(and(rejected)(crashed), error), assertCrashed(and(crashed)(resolved), error), ]); }); test('cancels the running Future', function (done){ var m = Future(function (){ return function (){ return done() } }); var cancel = and(m)(m)._interpret(done, noop, noop); cancel(); }); test('displays correctly as string', function (){ eq(and(rejected)(resolved).toString(), 'and (reject ("rejected")) (resolve ("resolved"))'); }); ================================================ FILE: test/unit/4.ap.js ================================================ import Either from 'sanctuary-either'; import {Future, ap, resolve, reject, after} from '../../index.js'; import {assertCrashed, assertRejected, assertResolved, assertValidFuture, noop, add, eq, bang, test} from '../util/util.js'; import {testFunction, applyArg, futureArg} from '../util/props.js'; testFunction('ap', ap, [applyArg, futureArg], assertValidFuture); test('dispatches to Fantasy Land ap', function (){ eq(ap(Either.Right('hello'))(Either.Right(bang)), Either.Right('hello!')); }); test('crashes when the other does not resolve to a Function', function (){ var m = ap(resolve(1))(resolve(null)); return assertCrashed(m, new TypeError( 'ap expects the second Future to resolve to a Function\n' + ' Actual: null' )); }); test('applies the Function on the right to the value on the left', function (){ return Promise.all([ assertResolved(ap(resolve(1))(resolve(add(1))), 2), assertRejected(ap(resolve(add(1)))(reject('err')), 'err'), assertRejected(ap(reject('err'))(resolve(add(1))), 'err'), assertResolved(ap(after(20)(1))(resolve(add(1))), 2), assertResolved(ap(resolve(1))(after(20)(add(1))), 2), ]); }); test('cancels the left Future if cancel is called while it is running', function (done){ var left = Future(function (){ return function (){ return done() } }); var right = resolve(add(1)); var cancel = ap(left)(right)._interpret(done, noop, noop); cancel(); }); test('cancels the right Future if cancel is called while it is running', function (done){ var left = resolve(1); var right = Future(function (){ return function (){ return done() } }); var cancel = ap(left)(right)._interpret(done, noop, noop); cancel(); }); test('displays correctly as string', function (){ eq(ap(resolve(1))(resolve(2)).toString(), 'ap (resolve (1)) (resolve (2))'); }); ================================================ FILE: test/unit/4.bichain.js ================================================ import {bichain} from '../../index.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, eq, throwing, error} from '../util/util.js'; import {testFunction, functionArg, futureArg} from '../util/props.js'; import {resolvedSlow, resolved, rejected, rejectedSlow} from '../util/futures.js'; import {resolve, reject} from '../../index.js'; testFunction('bichain', bichain, [functionArg, functionArg, futureArg], assertValidFuture); test('runs a bichain transformation on Futures', function (){ return Promise.all([ assertResolved(bichain(reject)(resolve)(resolved), 'resolved'), assertResolved(bichain(reject)(resolve)(resolvedSlow), 'resolvedSlow'), assertRejected(bichain(resolve)(reject)(resolved), 'resolved'), assertRejected(bichain(resolve)(reject)(resolvedSlow), 'resolvedSlow'), assertResolved(bichain(resolve)(reject)(rejected), 'rejected'), assertResolved(bichain(resolve)(reject)(rejectedSlow), 'rejectedSlow'), assertRejected(bichain(reject)(resolve)(rejected), 'rejected'), assertRejected(bichain(reject)(resolve)(rejectedSlow), 'rejectedSlow'), assertCrashed(bichain(throwing)(reject)(rejected), error), assertCrashed(bichain(resolve)(throwing)(resolved), error), ]); }); test('displays correctly as string', function (){ eq(bichain(resolve)(reject)(resolved).toString(), 'bichain (' + resolve.toString() + ') (' + reject.toString() + ') (resolve ("resolved"))'); }); ================================================ FILE: test/unit/4.bimap.js ================================================ import Either from 'sanctuary-either'; import {bimap} from '../../index.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, I, bang, eq, throwing, error} from '../util/util.js'; import {testFunction, functionArg, bifunctorArg} from '../util/props.js'; import {resolved, rejected} from '../util/futures.js'; testFunction('bimap', bimap, [functionArg, functionArg, bifunctorArg], assertValidFuture); test('runs a bimap transformation on Futures', function (){ return Promise.all([ assertRejected(bimap(bang)(I)(rejected), 'rejected!'), assertResolved(bimap(I)(bang)(resolved), 'resolved!'), assertCrashed(bimap(throwing)(I)(rejected), error), assertCrashed(bimap(I)(throwing)(resolved), error), ]); }); test('dispatches to Fantasy Land bimap otherwise', function (){ eq(bimap(I)(bang)(Either.Right('hello')), Either.Right('hello!')); }); test('displays correctly as string', function (){ eq(bimap(I)(bang)(resolved).toString(), 'bimap (' + I.toString() + ') (' + bang.toString() + ') (resolve ("resolved"))'); }); ================================================ FILE: test/unit/4.both.js ================================================ import {Future, both, node, done} from '../../index.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, error, noop, eq} from '../util/util.js'; import {crashed, rejected, resolved, crashedSlow, rejectedSlow, resolvedSlow} from '../util/futures.js'; import {testFunction, futureArg} from '../util/props.js'; testFunction('both', both, [futureArg, futureArg], assertValidFuture); test('resolves to a tuple of both resolution values', function (){ return Promise.all([ assertCrashed(both(crashed)(crashed), error), assertCrashed(both(rejected)(crashed), error), assertCrashed(both(crashed)(resolved), error), assertCrashed(both(resolved)(crashed), error), assertRejected(both(rejected)(rejected), 'rejected'), assertRejected(both(rejected)(rejectedSlow), 'rejected'), assertRejected(both(rejectedSlow)(rejected), 'rejected'), assertRejected(both(crashed)(rejected), 'rejected'), assertRejected(both(rejected)(crashedSlow), 'rejected'), assertRejected(both(resolved)(rejected), 'rejected'), assertRejected(both(rejected)(resolved), 'rejected'), assertResolved(both(resolved)(resolved), ['resolved', 'resolved']), assertResolved(both(resolved)(resolvedSlow), ['resolved', 'resolvedSlow']), assertResolved(both(resolvedSlow)(resolved), ['resolvedSlow', 'resolved']), assertResolved(both(resolvedSlow)(resolvedSlow), ['resolvedSlow', 'resolvedSlow']), ]); }); test('[GH #118] does not call the left computation twice', function (cb){ var called = false; var left = node(function (f){ called ? cb(error) : setTimeout(f, 20, null, called = true) }); return done(cb)(both(left)(resolvedSlow)); }); test('[GH #118] does not call the right computation twice', function (cb){ var called = false; var right = node(function (f){ called ? cb(error) : setTimeout(f, 20, null, called = true) }); return done(cb)(both(resolvedSlow)(right)); }); test('cancels the right if the left rejects', function (done){ var m = both(rejectedSlow)(Future(function (){ return function (){ return done() } })); m._interpret(done, noop, noop); }); test('cancels the left if the right rejects', function (done){ var m = both(Future(function (){ return function (){ return done() } }))(rejectedSlow); m._interpret(done, noop, noop); }); test('creates a cancel function which cancels both Futures', function (done){ var cancelled = false; var m = Future(function (){ return function (){ return (cancelled ? done() : (cancelled = true)) } }); var cancel = both(m)(m)._interpret(done, noop, noop); cancel(); }); test('displays correctly as string', function (){ eq(both(rejected)(resolved).toString(), 'both (reject ("rejected")) (resolve ("resolved"))'); }); ================================================ FILE: test/unit/4.chain-rej.js ================================================ import {chainRej, resolve, reject} from '../../index.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, bang, throwing, error, eq} from '../util/util.js'; import {rejected, resolved, rejectedSlow} from '../util/futures.js'; import {testFunction, functionArg, futureArg} from '../util/props.js'; testFunction('chainRej', chainRej, [functionArg, futureArg], assertValidFuture); test('crashes when the given function does not return Future', function (){ return assertCrashed(chainRej(bang)(rejected), new TypeError( 'chainRej expects the return value from the function it\'s given to be a valid Future.\n' + ' Actual: "rejected!" :: String\n' + ' When called with: "rejected"' )); }); test('calls the function with the rejection reason and sequences the returned Future', function (){ return Promise.all([ assertResolved(chainRej(resolve)(rejected), 'rejected'), assertResolved(chainRej(resolve)(rejectedSlow), 'rejectedSlow'), assertResolved(chainRej(reject)(resolved), 'resolved'), assertRejected(chainRej(reject)(rejected), 'rejected'), assertCrashed(chainRej(throwing)(rejected), error), ]); }); test('displays correctly as string', function (){ eq(chainRej(resolve)(resolved).toString(), 'chainRej (' + resolve.toString() + ') (resolve ("resolved"))'); }); ================================================ FILE: test/unit/4.chain.js ================================================ import Either from 'sanctuary-either'; import {chain, resolve, reject} from '../../index.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, bang, eq, throwing, error} from '../util/util.js'; import {rejected, resolved, resolvedSlow} from '../util/futures.js'; import {testFunction, functionArg, chainArg} from '../util/props.js'; testFunction('chain', chain, [functionArg, chainArg], assertValidFuture); test('dispatches to Fantasy Land chain', function (){ eq(chain(Either.Left)(Either.Right(42)), Either.Left(42)); }); test('crashes when the given function does not return Future', function (){ return assertCrashed(chain(bang)(resolved), new TypeError( 'chain expects the return value from the function it\'s given to be a valid Future.\n' + ' Actual: "resolved!" :: String\n' + ' When called with: "resolved"' )); }); test('calls the function with the resolution value and sequences the returned Future', function (){ return Promise.all([ assertRejected(chain(reject)(resolved), 'resolved'), assertRejected(chain(reject)(resolvedSlow), 'resolvedSlow'), assertResolved(chain(resolve)(resolved), 'resolved'), assertRejected(chain(resolve)(rejected), 'rejected'), assertCrashed(chain(throwing)(resolved), error), ]); }); test('displays correctly as string', function (){ eq(chain(resolve)(resolved).toString(), 'chain (' + resolve.toString() + ') (resolve ("resolved"))'); }); ================================================ FILE: test/unit/4.coalesce.js ================================================ import {coalesce} from '../../index.js'; import {test, assertCrashed, assertResolved, assertValidFuture, bang, B, throwing, error, eq} from '../util/util.js'; import {resolved, rejected} from '../util/futures.js'; import {testFunction, functionArg, futureArg} from '../util/props.js'; testFunction('coalesce', coalesce, [functionArg, functionArg, futureArg], assertValidFuture); test('joins the rejection and resolution values into the resolution branch', function (){ return Promise.all([ assertResolved(coalesce(bang)(B(bang)(bang))(rejected), 'rejected!'), assertResolved(coalesce(bang)(B(bang)(bang))(resolved), 'resolved!!'), assertCrashed(coalesce(throwing)(B)(rejected), error), assertCrashed(coalesce(B)(throwing)(resolved), error), ]); }); test('displays correctly as string', function (){ eq(coalesce(bang)(B)(resolved).toString(), 'coalesce (' + bang.toString() + ') (' + B.toString() + ') (resolve ("resolved"))'); }); ================================================ FILE: test/unit/4.lastly.js ================================================ import {lastly, resolve, reject, map} from '../../index.js'; import {test, assertRejected, assertResolved, assertValidFuture, noop, eq} from '../util/util.js'; import {rejected, rejectedSlow, resolved, resolvedSlow} from '../util/futures.js'; import {testFunction, futureArg} from '../util/props.js'; testFunction('lastly', lastly, [futureArg, futureArg], assertValidFuture); test('runs the second Future when the first resolves', function (done){ lastly(map(done)(resolve(null)))(resolve(1))._interpret(done, noop, noop); }); test('runs the second Future when the first rejects', function (done){ lastly(map(done)(resolve(null)))(reject(1))._interpret(done, noop, noop); }); test('resolves with the resolution value of the first', function (){ var actual = lastly(resolve(2))(resolve(1)); return assertResolved(actual, 1); }); test('rejects with the rejection reason of the first if the second resolves', function (){ var actual = lastly(resolve(2))(reject(1)); return assertRejected(actual, 1); }); test('always rejects with the rejection reason of the second', function (){ var actualResolved = lastly(reject(2))(resolve(1)); var actualRejected = lastly(reject(2))(reject(1)); return Promise.all([ assertRejected(actualResolved, 2), assertRejected(actualRejected, 2), ]); }); test('does nothing after being cancelled', function (done){ const fail = () => fail(done); lastly(resolved)(resolvedSlow)._interpret(done, fail, fail)(); lastly(resolvedSlow)(resolved)._interpret(done, fail, fail)(); lastly(rejected)(rejectedSlow)._interpret(done, fail, fail)(); lastly(rejectedSlow)(rejected)._interpret(done, fail, fail)(); setTimeout(done, 25); }); test('displays correctly as string', function (){ eq(lastly(rejected)(resolved).toString(), 'lastly (reject ("rejected")) (resolve ("resolved"))'); }); ================================================ FILE: test/unit/4.map-rej.js ================================================ import {mapRej} from '../../index.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, bang, throwing, error, eq} from '../util/util.js'; import {rejected, resolved, resolvedSlow, rejectedSlow} from '../util/futures.js'; import {testFunction, functionArg, futureArg} from '../util/props.js'; testFunction('mapRej', mapRej, [functionArg, futureArg], assertValidFuture); test('maps the rejection branch with the given function', function (){ return Promise.all([ assertRejected(mapRej(bang)(rejected), 'rejected!'), assertResolved(mapRej(bang)(resolved), 'resolved'), assertCrashed(mapRej(throwing)(rejected), error), ]); }); test('does not resolve after being cancelled', function (done){ const fail = () => done(error); mapRej(fail)(resolvedSlow)._interpret(done, fail, fail)(); setTimeout(done, 25); }); test('does not reject after being cancelled', function (done){ const fail = () => done(error); mapRej(fail)(rejectedSlow)._interpret(done, fail, fail)(); setTimeout(done, 25); }); test('displays correctly as string', function (){ eq(mapRej(bang)(rejected).toString(), 'mapRej (' + bang.toString() + ') (reject ("rejected"))'); }); ================================================ FILE: test/unit/4.map.js ================================================ import Either from 'sanctuary-either'; import {map} from '../../index.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, bang, eq, throwing, error} from '../util/util.js'; import {rejected, resolved, resolvedSlow, rejectedSlow} from '../util/futures.js'; import {testFunction, functionArg, functorArg} from '../util/props.js'; testFunction('map', map, [functionArg, functorArg], assertValidFuture); test('dispatches to Fantasy Land map', function (){ eq(map(bang)(Either.Right('hello')), Either.Right('hello!')); }); test('maps the resolution branch with the given function', function (){ return Promise.all([ assertRejected(map(bang)(rejected), 'rejected'), assertResolved(map(bang)(resolved), 'resolved!'), assertCrashed(map(throwing)(resolved), error), ]); }); test('does not resolve after being cancelled', function (done){ const fail = () => done(error); map(fail)(resolvedSlow)._interpret(done, fail, fail)(); setTimeout(done, 25); }); test('does not reject after being cancelled', function (done){ const fail = () => done(error); map(fail)(rejectedSlow)._interpret(done, fail, fail)(); setTimeout(done, 25); }); test('displays correctly as string', function (){ eq(map(bang)(resolved).toString(), 'map (' + bang.toString() + ') (resolve ("resolved"))'); }); ================================================ FILE: test/unit/4.pap.js ================================================ import {Future, resolve, reject, after} from '../../index.js'; import {pap} from '../../src/pap.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, noop, add} from '../util/util.js'; import {testFunction, futureArg} from '../util/props.js'; testFunction('pap', pap, [futureArg, futureArg], assertValidFuture); test('crashes when the other does not resolve to a Function', function (){ var m = pap(resolve(1))(resolve(null)); return assertCrashed(m, new TypeError( 'pap expects the second Future to resolve to a Function\n' + ' Actual: null' )); }); test('applies the Function on the right to the value on the left', function (){ return Promise.all([ assertResolved(pap(resolve(1))(resolve(add(1))), 2), assertRejected(pap(resolve(add(1)))(reject('err')), 'err'), assertRejected(pap(reject('err'))(resolve(add(1))), 'err'), assertResolved(pap(after(20)(1))(resolve(add(1))), 2), assertResolved(pap(resolve(1))(after(20)(add(1))), 2), ]); }); test('cancels the left Future if cancel is called while it is running', function (done){ var left = Future(function (){ return function (){ return done() } }); var right = resolve(add(1)); var cancel = pap(left)(right)._interpret(done, noop, noop); cancel(); }); test('cancels the right Future if cancel is called while it is running', function (done){ var left = resolve(1); var right = Future(function (){ return function (){ return done() } }); var cancel = pap(left)(right)._interpret(done, noop, noop); cancel(); }); ================================================ FILE: test/unit/4.race.js ================================================ import {Future, race} from '../../index.js'; import {test, assertCrashed, assertRejected, assertResolved, assertValidFuture, error, noop, eq} from '../util/util.js'; import {crashed, crashedSlow, rejected, rejectedSlow, resolved, resolvedSlow} from '../util/futures.js'; import {testFunction, futureArg} from '../util/props.js'; testFunction('race', race, [futureArg, futureArg], assertValidFuture); test('races one Future against another', function (){ return Promise.all([ assertCrashed(race(crashed)(resolvedSlow), error), assertResolved(race(crashedSlow)(resolved), 'resolved'), assertCrashed(race(crashed)(rejectedSlow), error), assertRejected(race(crashedSlow)(rejected), 'rejected'), assertResolved(race(resolved)(crashedSlow), 'resolved'), assertCrashed(race(resolvedSlow)(crashed), error), assertRejected(race(rejected)(crashedSlow), 'rejected'), assertCrashed(race(rejectedSlow)(crashed), error), assertResolved(race(resolved)(resolvedSlow), 'resolved'), assertResolved(race(resolvedSlow)(resolved), 'resolved'), assertRejected(race(rejectedSlow)(rejected), 'rejected'), assertRejected(race(rejected)(rejectedSlow), 'rejected'), assertResolved(race(rejectedSlow)(resolved), 'resolved'), assertRejected(race(rejected)(resolvedSlow), 'rejected'), assertResolved(race(resolved)(rejectedSlow), 'resolved'), assertRejected(race(resolvedSlow)(rejected), 'rejected'), ]); }); test('cancels the right if the left resolves', function (done){ var m = race(resolvedSlow)(Future(function (){ return function (){ return done() } })); m._interpret(done, noop, noop); }); test('cancels the left if the right resolves', function (done){ var m = race(Future(function (){ return function (){ return done() } }))(resolvedSlow); m._interpret(done, noop, noop); }); test('cancels the right if the left rejects', function (done){ var m = race(rejectedSlow)(Future(function (){ return function (){ return done() } })); m._interpret(done, noop, noop); }); test('cancels the left if the right rejects', function (done){ var m = race(Future(function (){ return function (){ return done() } }))(rejectedSlow); m._interpret(done, noop, noop); }); test('creates a cancel function which cancels both Futures', function (done){ var cancelled = false; var m = Future(function (){ return function (){ return (cancelled ? done() : (cancelled = true)) } }); var cancel = race(m)(m)._interpret(done, noop, noop); cancel(); }); test('displays correctly as string', function (){ eq(race(rejected)(resolved).toString(), 'race (reject ("rejected")) (resolve ("resolved"))'); }); ================================================ FILE: test/unit/4.swap.js ================================================ import {swap, resolve, reject} from '../../index.js'; import {test, assertRejected, assertResolved, assertValidFuture, eq} from '../util/util.js'; import {testFunction, futureArg} from '../util/props.js'; testFunction('swap', swap, [futureArg], assertValidFuture); test('rejects with the resolution value', function (){ var actual = swap(resolve(1)); return assertRejected(actual, 1); }); test('resolves with the rejection reason', function (){ var actual = swap(reject(1)); return assertResolved(actual, 1); }); test('displays correctly as string', function (){ eq(swap(resolve(42)).toString(), 'swap (resolve (42))'); }); ================================================ FILE: test/unit/5.chain-rec.js ================================================ import chai from 'chai'; import {resolve, after, reject} from '../../index.js'; import {chainRec} from '../../src/future.js'; import {isIteration} from '../../src/internal/iteration.js'; import {test, assertCrashed, assertRejected, assertResolved, error, noop} from '../util/util.js'; var expect = chai.expect; test('is a binary function', function (){ expect(chainRec).to.be.a('function'); expect(chainRec.length).to.equal(2); }); test('crashes if the iterator throws', function (){ var m = chainRec(function (){ throw error }); return assertCrashed(m, error); }); test('does not break if the iteration does not contain a value key', function (){ var actual = chainRec(function (f, g, x){ return (x, resolve({done: true})) }, 0); return assertResolved(actual, undefined); }); test('calls the function with Next, Done and the initial value', function (){ chainRec(function (next, done, x){ expect(next).to.be.a('function'); expect(next.length).to.equal(1); expect(next(x)).to.satisfy(isIteration); expect(done).to.be.a('function'); expect(done.length).to.equal(1); expect(done(x)).to.satisfy(isIteration); expect(x).to.equal(42); return resolve(done(x)); }, 42)._interpret(noop, noop, noop); }); test('calls the function with the value from the current iteration', function (){ var i = 0; chainRec(function (f, g, x){ expect(x).to.equal(i); return x < 5 ? resolve(f(++i)) : resolve(g(x)); }, i)._interpret(noop, noop, noop); }); test('works asynchronously', function (){ var actual = chainRec(function (f, g, x){ return after(10)(x < 5 ? f(x + 1) : g(x)) }, 0); return assertResolved(actual, 5); }); test('responds to failure', function (){ var m = chainRec(function (f, g, x){ return reject(x) }, 1); return assertRejected(m, 1); }); test('responds to failure after chaining async', function (){ var m = chainRec( function (f, g, x){ return x < 2 ? after(10)(f(x + 1)) : reject(x) }, 0 ); return assertRejected(m, 2); }); test('can be cancelled straight away', function (done){ const fail = () => done(error); chainRec(function (f, g, x){ return after(10)(g(x)) }, 1) ._interpret(done, fail, fail)(); setTimeout(done, 20); }); test('can be cancelled after some iterations', function (done){ const fail = () => done(error); var m = chainRec(function (f, g, x){ return after(10)(x < 5 ? f(x + 1) : g(x)) }, 0); var cancel = m._interpret(done, fail, fail); setTimeout(cancel, 25); setTimeout(done, 70); }); ================================================ FILE: test/unit/5.par.js ================================================ import chai from 'chai'; import Z from 'sanctuary-type-classes'; import {Future, Par, seq, resolve, reject, never, ap, map, alt, and} from '../../index.js'; import {test, add, assertCrashed, assertRejected, assertResolved, bang, error, noop, throws} from '../util/util.js'; import {rejected, resolved, resolvedSlow} from '../util/futures.js'; var expect = chai.expect; var mf = resolve(bang); test('is a unary function', function (){ expect(Par).to.be.a('function'); expect(Par.length).to.equal(1); }); test('throws when not given a Future', function (){ var f = function (){ return Par(1) }; throws(f, new TypeError( 'Par() expects its first argument to be a valid Future.\n' + ' Actual: 1 :: Number' )); }); test('of resolves with the value', function (){ var m = Z.of(Par, 1); return assertResolved(seq(m), 1); }); test('zero creates a never-ending ConcurrentFuture', function (){ var m = Z.zero(Par); expect(seq(m)).to.equal(never); }); test('ap throws TypeError when the Future does not resolve to a Function', function (){ var m = seq(ap(Par(resolved))(Par(resolve(1)))); return assertCrashed(m, new TypeError( 'pap expects the second Future to resolve to a Function\n' + ' Actual: 1' )); }); test('ap calls the function contained in the given Future to its contained value', function (){ var actual = ap(Par(resolved))(Par(mf)); return assertResolved(seq(actual), 'resolved!'); }); test('ap rejects if one of the two reject', function (){ var left = ap(Par(rejected))(Par(mf)); var right = ap(Par(resolved))(Par(rejected)); return Promise.all([ assertRejected(seq(left), 'rejected'), assertRejected(seq(right), 'rejected'), ]); }); test('ap does not matter if either resolves late', function (){ var left = ap(Par(resolvedSlow))(Par(mf)); var right = ap(Par(resolved))(Par(and(mf)(resolvedSlow))); return Promise.all([ assertResolved(seq(left), 'resolvedSlow!'), assertResolved(seq(right), 'resolved!'), ]); }); test('ap cannot reject twice', function (){ var actual = ap(Par(rejected))(Par(rejected)); return assertRejected(seq(actual), 'rejected'); }); test('ap creates a cancel function which cancels both Futures', function (done){ var cancelled = false; var m = Par(Future(function (){ return function (){ return (cancelled ? done() : (cancelled = true)) } })); var cancel = seq(ap(m)(m))._interpret(done, noop, noop); cancel(); }); test('ap shows a reasonable representation when cast to string', function (){ var m = ap(Par(reject(0)))(Par(resolve(1))); var s = 'Par (pap (reject (0)) (resolve (1)))'; expect(m.toString()).to.equal(s); }); test('map applies the given function to its inner', function (){ var actual = map(add(1))(Par(resolve(1))); return assertResolved(seq(actual), 2); }); test('map does not map rejected state', function (){ var actual = map(function (){ return 'mapped' })(Par(rejected)); return assertRejected(seq(actual), 'rejected'); }); test('map shows a reasonable representation when cast to string', function (){ var m = map(noop)(Par(resolved)); var expected = 'Par (map (' + noop.toString() + ') (resolve ("resolved")))'; expect(m.toString()).to.equal(expected); }); test('alt rejects when the first one rejects', function (){ var m1 = Par(Future(function (rej, res){ setTimeout(res, 50, 1); return noop })); var m2 = Par(Future(function (rej){ setTimeout(rej, 5, error); return noop })); return assertRejected(seq(alt(m1)(m2)), error); }); test('alt resolves when the first one resolves', function (){ var m1 = Par(Future(function (rej, res){ setTimeout(res, 5, 1); return noop })); var m2 = Par(Future(function (rej){ setTimeout(rej, 50, error); return noop })); return assertResolved(seq(alt(m1)(m2)), 1); }); test('alt shows a reasonable representation when cast to string', function (){ var m = alt(Par(resolve(1)))(Par(resolve(2))); var s = 'Par (race (resolve (1)) (resolve (2)))'; expect(m.toString()).to.equal(s); }); ================================================ FILE: test/unit/5.seq.js ================================================ import {seq} from '../../index.js'; import {testFunction, parallelArg} from '../util/props.js'; import {assertValidFuture} from '../util/util.js'; testFunction('seq', seq, [parallelArg], assertValidFuture); ================================================ FILE: test/util/futures.js ================================================ import {Future, resolve, reject, after, rejectAfter, and} from '../../index.js'; import {crash} from '../../src/future.js'; import {error} from '../util/util.js'; export var mock = Object.create(Future.prototype); mock._interpret = function (){ throw new Error('Override _interpret on mock Future') }; mock.toString = function (){ return '(util.mock)' }; export var resolved = resolve('resolved'); export var rejected = reject('rejected'); export var resolvedSlow = after(20)('resolvedSlow'); export var rejectedSlow = rejectAfter(20)('rejectedSlow'); export var crashed = crash(error); export var crashedSlow = and(crashed)(after(20)(null)); ================================================ FILE: test/util/props.js ================================================ import type from 'sanctuary-type-identifiers'; import show from 'sanctuary-show'; import jsc from 'jsverify'; import {ordinal} from '../../src/internal/const.js'; import {eq, error, throws, test} from './util.js'; import { any, anyFuture, anyNonFuture, anyParallel, anyFunction, anyResolvedFuture, FutureArb, } from '../arbitraries.js'; export * from '../arbitraries.js'; export var array = jsc.array; export var nearray = jsc.nearray; export var bool = jsc.bool; export var constant = jsc.constant; export var falsy = jsc.falsy; export var fn = jsc.fn; export var letrec = jsc.letrec; export var nat = jsc.nat; export var number = jsc.number; export var oneof = jsc.oneof; export var string = jsc.string; export var elements = jsc.elements; export var suchthat = jsc.suchthat; export function _of (rarb){ return FutureArb(string, rarb); } export function property (name){ const args = Array.from(arguments).slice(1); test(name, () => { return jsc.assert(jsc.forall.apply(null, args)); }); } export function f (x){ return {f: x}; } export function g (x){ return {g: x}; } export var altArg = { name: 'have Alt implemented', valid: anyFuture, invalid: anyNonFuture, }; export var applyArg = { name: 'have Apply implemented', valid: anyFuture, invalid: anyNonFuture, }; export var bifunctorArg = { name: 'have Bifunctor implemented', valid: anyFuture, invalid: anyNonFuture, }; export var chainArg = { name: 'have Chain implemented', valid: anyFuture, invalid: anyNonFuture, }; export var functorArg = { name: 'have Functor implemented', valid: anyFuture, invalid: anyNonFuture, }; export var functionArg = { name: 'be a Function', valid: anyFunction, invalid: oneof(number, string, bool, falsy, constant(error)), }; export var futureArg = { name: 'be a valid Future', valid: anyFuture, invalid: anyNonFuture, }; export var resolvedFutureArg = { name: 'be a valid Future', valid: anyResolvedFuture, invalid: anyNonFuture, }; export var positiveIntegerArg = { name: 'be a positive Integer', valid: suchthat(nat, function (x){ return x > 0 }), invalid: oneof(bool, constant(0.5)), }; export var futureArrayArg = { name: 'be an Array of valid Futures', valid: array(anyFuture), invalid: oneof(nearray(anyNonFuture), any), }; export var parallelArg = { name: 'be a ConcurrentFuture', valid: anyParallel, invalid: any, }; export var anyArg = { name: 'be anything', valid: any, invalid: null, }; var getValid = function (x){ return x.valid }; var generateValid = function (x){ return getValid(x).generator(1) }; var capply = function (f, args){ return args.reduce(function (g, x){ return g(x) }, f); }; export function testFunction (name, func, args, assert){ var validArbs = args.map(getValid); var validArgs = args.map(generateValid); test('is a curried ' + args.length + '-ary function', function (){ eq(typeof func, 'function'); eq(func.length, 1); validArgs.slice(0, -1).forEach(function (_, idx){ var partial = capply(func, validArgs.slice(0, idx + 1)); eq(typeof partial, 'function'); eq(partial.length, 1); }); }); args.forEach(function (arg, idx){ var priorArgs = args.slice(0, idx); var followingArgs = args.slice(idx + 1); var validPriorArgs = priorArgs.map(generateValid); var validFollowingArgs = followingArgs.map(generateValid); if(arg !== anyArg){ property('throws when the ' + ordinal[idx] + ' argument is invalid', arg.invalid, function (value){ throws(function (){ capply(func, validPriorArgs.concat([value]).concat(validFollowingArgs)); }, new TypeError( name + '() expects its ' + ordinal[idx] + ' argument to ' + arg.name + '.\n' + ' Actual: ' + show(value) + ' :: ' + type.parse(type(value)).name )); return true; }); property('throws when the ' + ordinal[idx] + ' invocation has more than one argument', arg.valid, function (value){ throws(function (){ var partial = capply(func, validPriorArgs); partial(value, 42); }, new TypeError( name + '() expects to be called with a single argument per invocation\n' + ' Saw: 2 arguments\n' + ' First: ' + show(value) + ' :: ' + type.parse(type(value)).name + '\n' + ' Second: 42 :: Number' )); return true; }); } }); property.apply(null, ['returns valid output when given valid input'].concat(validArbs).concat([function (){ return assert(capply(func, Array.from(arguments))); }])); } ================================================ FILE: test/util/util.js ================================================ import oletus from 'oletus'; import show from 'sanctuary-show'; import {reject, resolve} from '../../index.js'; import {crash} from '../../src/future.js'; import * as assert from '../assertions.js'; export * from '../../src/internal/predicates.js'; export var STACKSIZE = (function r (){ try{ return 1 + r() }catch(e){ return 1 } }()); export var noop = function (){}; export var add = function (a){ return function (b){ return a + b } }; export var sub = function (a){ return function (b){ return a - b } }; export var bang = function (s){ return (s + '!') }; export var I = function (x){ return x }; export var B = function (f){ return function (g){ return function (x){ return f(g(x)) } } }; export var K = function (x){ return function (){ return x } }; export var T = function (x){ return function (f){ return f(x) } }; export var error = new Error('Intentional error for unit testing'); export var throwing = function (){ throw error }; export function test (name, impl){ oletus(name, () => ( impl.length === 0 ? impl() : new Promise((res, rej) => { impl(e => e ? rej(e) : res()) }) )); } export var eq = function eq (actual, expected){ assert.equality(actual)(expected); }; export var throws = function throws (f, expected){ try{ f(); }catch(actual){ eq(typeof actual, typeof expected); eq(actual.constructor, expected.constructor); eq(actual.name, expected.name); eq(actual.message, expected.message); return; } throw new Error('Expected the function to throw'); }; export var itRaises = function itRaises (when, f, e){ test('raises ' + when, function (done){ var listeners = process.rawListeners('uncaughtException'); process.removeAllListeners('uncaughtException'); process.once('uncaughtException', function (actual){ listeners.forEach(function (f){ process.on('uncaughtException', f) }); try { eq(actual.message, e.message); }catch(err){ done(err); return; } done(); }); f(); }); }; export var isDeepStrictEqual = function isDeepStrictEqual (actual, expected){ try{ eq(actual, expected); return true; }catch(e){ return false; } }; export var repeat = function (n, x){ var out = new Array(n); while(n-- > 0){ out[n] = x } //eslint-disable-line return out; }; export var promiseTimeout = function (t, p){ return Promise.race([ p, new Promise((res, rej) => { setTimeout(rej, t, new Error(`Timeout of ${t}ms reached`)); }), ]); }; export var assertIsFuture = function (x){ return assert.future(x); }; export var assertValidFuture = function (x){ assertIsFuture(x); eq(typeof x.extractLeft, 'function'); eq(x.extractLeft.length, 0); eq(Array.isArray(x.extractLeft()), true); eq(typeof x.extractRight, 'function'); eq(x.extractRight.length, 0); eq(Array.isArray(x.extractRight()), true); eq(typeof x._transform, 'function'); eq(x._transform.length, 1); eq(typeof x._interpret, 'function'); eq(typeof x._interpret(noop, noop, noop), 'function'); eq(x._interpret(noop, noop, noop).length, 0); eq(x._interpret(noop, noop, noop)(), undefined); return true; }; export var assertEqual = function (a, b){ return assert.equivalence(a)(b); }; var assertEqualByErrorMessage = assert.makeEquivalence(a => b => { return assert.equality(a.message)(b.message); }); export var assertCrashed = function (m, x){ return assertEqualByErrorMessage(m)(crash(x)); }; export var assertRejected = function (m, x){ return assertEqual(m, reject(x)); }; export var assertResolved = function (m, x){ return assertEqual(m, resolve(x)); }; export var onceOrError = function (f){ var called = false; return function (){ if(called){ throw new Error('Function ' + show(f) + ' was called twice') } called = true; return f.apply(null, arguments); }; }; export function assertStackTrace (name, x){ eq(typeof x, 'string'); eq(x.slice(0, name.length), name); var lines = x.slice(name.length).split('\n'); eq(lines.length > 0, true); }