├── .config
├── .eslintrc.json
├── .gitignore
├── .npmrc
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── index.js
├── package.json
└── test
├── .eslintrc.json
└── concurrify.test.js
/.config:
--------------------------------------------------------------------------------
1 | author-name = Aldwin Vlasblom
2 | repo-owner = fluture-js
3 | repo-name = concurrify
4 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["./node_modules/sanctuary-style/eslint-es3.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /coverage/
2 | /node_modules/
3 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "6.0.0"
4 | - "6"
5 | - "7"
6 | - "8"
7 | - "9"
8 | - "10"
9 | after_success: ./node_modules/.bin/codecov
10 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution guide
2 |
3 | ## Making a contribution
4 |
5 | * Fork and clone the project
6 | * Commit changes to a branch named after the work that was done
7 | * Make sure the tests pass locally
8 | * Create a pull request
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2019 Aldwin Vlasblom
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of
5 | this software and associated documentation files (the "Software"), to deal in
6 | the Software without restriction, including without limitation the rights to
7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 | of the Software, and to permit persons to whom the Software is furnished to do
9 | so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Concurrify
2 |
3 | ## :warning: Unmaintained
4 |
5 | This library is no longer used by Fluture ([`7b6d9fd`][]), and now fills a
6 | space I don't think is worth filling.
7 |
8 | 1. It's overly opinionated as a result of having been part of Fluture.
9 | 2. It does more than strictly necessary (inclusion of Alternative instance).
10 | 3. It doesn't do any real work - it just takes all the needed functions as
11 | input and wires them up for Fantasy Land compliance. As such, it's not
12 | a lot of effort for users to do the wiring themselves.
13 | 4. It used undocumented features from [Sanctuary Type Identifiers][STI] to
14 | automatically generate new type identifiers, but these are no longer
15 | available in the latest version of Sanctuary Type Identifiers.
16 |
17 | [`7b6d9fd`]: https://github.com/fluture-js/Fluture/commit/7b6d9fdc4ebbc4c6c2485cb5a8d1b2da1eb39fe4
18 | ----
19 |
20 | ## Introduction
21 |
22 | Turn non-concurrent [FantasyLand 3][FL3] Applicatives concurrent.
23 |
24 | Most time-dependent applicatives are very useful as Monads, because it
25 | gives them the ability to run sequentially, where each step depends on the
26 | previous. However, they lose the ability to run concurrently. This library
27 | allows one to wrap a [`Monad`][FL:Monad] (with sequential `ap`) in an
28 | [`Alternative`][FL:Alternative] (with parallel `ap`).
29 |
30 | ## Usage
31 |
32 | ```js
33 | // The concurrify function takes four arguments, explained below.
34 | const concurrify = require ('concurrify');
35 |
36 | // The Type Representative of the Applicative we want to transform.
37 | const Future = require ('fluture');
38 |
39 | // A "zero" instance and an "alt" function for "Alternative".
40 | const zero = Future (() => {});
41 | const alt = Future.race;
42 |
43 | // An override "ap" function that runs the Applicatives concurrently.
44 | const ap = (mx, mf) => (Future.both (mx, mf)).map (([x, f]) => f (x));
45 |
46 | // A new Type Representative created by concurrify.
47 | const ConcurrentFuture = concurrify (Future, zero, alt, ap);
48 |
49 | // We can use our type as such:
50 | const par = ConcurrentFuture (Future.of (1));
51 | const seq = par.sequential;
52 | seq.fork (console.error, console.log);
53 | ```
54 |
55 | ## Interoperability
56 |
57 | * Implements [FantasyLand 3][FL3] `Alternative`
58 | (`of`, `zero`, `map`, `ap`, `alt`).
59 | * Instances can be identified by, and are compared using,
60 | [Sanctuary Type Identifiers][STI].
61 | * Instances can be converted to String representations according to
62 | [Sanctuary Show][SS].
63 |
64 | ## API
65 |
66 | #### `concurrify :: (Applicative f, Alternative (m f)) => (TypeRep f, f a, (f a, f a) -> f a, (f a, f (a -> b)) -> f b) -> f c -> m f c`
67 |
68 | [FL3]: https://github.com/fantasyland/fantasy-land/
69 | [FL:Monad]: https://github.com/fantasyland/fantasy-land/#monad
70 | [FL:Alternative]: https://github.com/fantasyland/fantasy-land/#alternative
71 | [STI]: https://github.com/sanctuary-js/sanctuary-type-identifiers
72 | [SS]: https://github.com/sanctuary-js/sanctuary-show
73 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | //. # Concurrify
2 | //.
3 | //. [](https://gitter.im/fluture-js/fluture)
4 | //. [](https://www.npmjs.com/package/concurrify)
5 | //. [](https://david-dm.org/fluture-js/concurrify)
6 | //. [](https://travis-ci.org/fluture-js/concurrify)
7 | //. [](https://codecov.io/gh/fluture-js/concurrify)
8 | //.
9 | //. Turn non-concurrent [FantasyLand 3][FL3] Applicatives concurrent.
10 | //.
11 | //. Most time-dependent applicatives are very useful as Monads, because it
12 | //. gives them the ability to run sequentially, where each step depends on the
13 | //. previous. However, they lose the ability to run concurrently. This library
14 | //. allows one to wrap a [`Monad`][FL:Monad] (with sequential `ap`) in an
15 | //. [`Alternative`][FL:Alternative] (with parallel `ap`).
16 | //.
17 | //. ## Usage
18 | //.
19 | //. ```js
20 | //. // The concurrify function takes four arguments, explained below.
21 | //. const concurrify = require ('concurrify');
22 | //.
23 | //. // The Type Representative of the Applicative we want to transform.
24 | //. const Future = require ('fluture');
25 | //.
26 | //. // A "zero" instance and an "alt" function for "Alternative".
27 | //. const zero = Future (() => {});
28 | //. const alt = Future.race;
29 | //.
30 | //. // An override "ap" function that runs the Applicatives concurrently.
31 | //. const ap = (mx, mf) => (Future.both (mx, mf)).map (([x, f]) => f (x));
32 | //.
33 | //. // A new Type Representative created by concurrify.
34 | //. const ConcurrentFuture = concurrify (Future, zero, alt, ap);
35 | //.
36 | //. // We can use our type as such:
37 | //. const par = ConcurrentFuture (Future.of (1));
38 | //. const seq = par.sequential;
39 | //. seq.fork (console.error, console.log);
40 | //. ```
41 | //.
42 | //. ## Interoperability
43 | //.
44 | //. * Implements [FantasyLand 3][FL3] `Alternative`
45 | //. (`of`, `zero`, `map`, `ap`, `alt`).
46 | //. * Instances can be identified by, and are compared using,
47 | //. [Sanctuary Type Identifiers][STI].
48 | //. * Instances can be converted to String representations according to
49 | //. [Sanctuary Show][SS].
50 | //.
51 | //. ## API
52 | (function(f) {
53 |
54 | 'use strict';
55 |
56 | /* istanbul ignore next */
57 | if (typeof module === 'object' && typeof module.exports === 'object') {
58 | module.exports = f (
59 | require ('sanctuary-show'),
60 | require ('sanctuary-type-identifiers')
61 | );
62 | } else {
63 | self.concurrify = f (
64 | self.sanctuaryShow,
65 | self.sanctuaryTypeIdentifiers
66 | );
67 | }
68 |
69 | } (function(show, type) {
70 |
71 | 'use strict';
72 |
73 | var $alt = 'fantasy-land/alt';
74 | var $ap = 'fantasy-land/ap';
75 | var $map = 'fantasy-land/map';
76 | var $of = 'fantasy-land/of';
77 | var $zero = 'fantasy-land/zero';
78 | var $$type = '@@type';
79 | var $$show = '@@show';
80 | var ordinal = ['first', 'second', 'third', 'fourth', 'fifth'];
81 |
82 | // isFunction :: Any -> Boolean
83 | function isFunction(f) {
84 | return typeof f === 'function';
85 | }
86 |
87 | // isBinary :: Function -> Boolean
88 | function isBinary(f) {
89 | return f.length >= 2;
90 | }
91 |
92 | // isApplicativeRepr :: TypeRepr -> Boolean
93 | function isApplicativeRepr(Repr) {
94 | return typeof Repr[$of] === 'function' &&
95 | typeof Repr[$of] ()[$ap] === 'function';
96 | }
97 |
98 | // invalidArgument :: (String, Number, String, String) -> !Undefined
99 | function invalidArgument(it, at, expected, actual) {
100 | throw new TypeError (
101 | it
102 | + ' expects its '
103 | + ordinal[at]
104 | + ' argument to '
105 | + expected
106 | + '\n Actual: '
107 | + show (actual)
108 | );
109 | }
110 |
111 | // invalidContext :: (String, String, String) -> !Undefined
112 | function invalidContext(it, actual, an) {
113 | throw new TypeError (
114 | it
115 | + ' was invoked outside the context of a '
116 | + an
117 | + '. \n Called on: '
118 | + show (actual)
119 | );
120 | }
121 |
122 | // getTypeIdentifier :: TypeRepresentative -> String
123 | function getTypeIdentifier(Repr) {
124 | return Repr[$$type] || Repr.name || 'Anonymous';
125 | }
126 |
127 | // generateTypeIdentifier :: String -> String
128 | function generateTypeIdentifier(identifier) {
129 | var o = type.parse (identifier);
130 | return (
131 | (o.namespace || 'concurrify') + '/Concurrent' + o.name + '@' + o.version
132 | );
133 | }
134 |
135 | //# concurrify :: (Applicative f, Alternative (m f)) => (TypeRep f, f a, (f a, f a) -> f a, (f a, f (a -> b)) -> f b) -> f c -> m f c
136 | return function concurrify(Repr, zero, alt, ap) {
137 |
138 | var INNERTYPE = getTypeIdentifier (Repr);
139 | var OUTERTYPE = generateTypeIdentifier (INNERTYPE);
140 | var INNERNAME = (type.parse (INNERTYPE)).name;
141 | var OUTERNAME = (type.parse (OUTERTYPE)).name;
142 |
143 | function Concurrently(sequential) {
144 | this.sequential = sequential;
145 | }
146 |
147 | function isInner(x) {
148 | return (
149 | (x instanceof Repr) ||
150 | (Boolean (x) && x.constructor === Repr) ||
151 | (type (x) === Repr[$$type])
152 | );
153 | }
154 |
155 | function isOuter(x) {
156 | return (
157 | (x instanceof Concurrently) ||
158 | (Boolean (x) && x.constructor === Concurrently) ||
159 | (type (x) === OUTERTYPE)
160 | );
161 | }
162 |
163 | function construct(x) {
164 | if (!isInner (x)) {
165 | invalidArgument (OUTERNAME, 0, 'be of type "' + INNERNAME + '"', x);
166 | }
167 | return new Concurrently (x);
168 | }
169 |
170 | if (!isApplicativeRepr (Repr)) {
171 | invalidArgument ('concurrify', 0, 'represent an Applicative', Repr);
172 | }
173 |
174 | if (!isInner (zero)) {
175 | invalidArgument
176 | ('concurrify', 1, 'be of type "' + INNERNAME + '"', zero);
177 | }
178 |
179 | if (!isFunction (alt)) {
180 | invalidArgument ('concurrify', 2, 'be a function', alt);
181 | }
182 |
183 | if (!isBinary (alt)) {
184 | invalidArgument ('concurrify', 2, 'be binary', alt);
185 | }
186 |
187 | if (!isFunction (ap)) {
188 | invalidArgument ('concurrify', 3, 'be a function', ap);
189 | }
190 |
191 | if (!isBinary (ap)) {
192 | invalidArgument ('concurrify', 3, 'be binary', ap);
193 | }
194 |
195 | var proto =
196 | Concurrently.prototype =
197 | construct.prototype = {constructor: construct};
198 |
199 | proto[$$type] = OUTERTYPE;
200 | construct[$$type] = OUTERTYPE;
201 |
202 | var mzero = new Concurrently (zero);
203 |
204 | construct[$zero] = function Concurrently$zero() {
205 | return mzero;
206 | };
207 |
208 | construct[$of] = function Concurrently$of(value) {
209 | return new Concurrently (Repr[$of] (value));
210 | };
211 |
212 | proto[$map] = function Concurrently$map(mapper) {
213 | if (!isOuter (this)) {
214 | invalidContext (OUTERNAME + '#map', this, OUTERNAME);
215 | }
216 |
217 | if (!isFunction (mapper)) {
218 | invalidArgument (OUTERNAME + '#map', 0, 'be a function', mapper);
219 | }
220 |
221 | return new Concurrently (this.sequential[$map] (mapper));
222 | };
223 |
224 | proto[$ap] = function Concurrently$ap(m) {
225 | if (!isOuter (this)) {
226 | invalidContext (OUTERNAME + '#ap', this, OUTERNAME);
227 | }
228 |
229 | if (!isOuter (m)) {
230 | invalidArgument (OUTERNAME + '#ap', 0, 'be a ' + OUTERNAME, m);
231 | }
232 |
233 | return new Concurrently (ap (this.sequential, m.sequential));
234 | };
235 |
236 | proto[$alt] = function Concurrently$alt(m) {
237 | if (!isOuter (this)) {
238 | invalidContext (OUTERNAME + '#alt', this, OUTERNAME);
239 | }
240 |
241 | if (!isOuter (m)) {
242 | invalidArgument (OUTERNAME + '#alt', 0, 'be a ' + OUTERNAME, m);
243 | }
244 |
245 | return new Concurrently (alt (this.sequential, m.sequential));
246 | };
247 |
248 | proto[$$show] = function Concurrently$show() {
249 | return OUTERNAME + '(' + show (this.sequential) + ')';
250 | };
251 |
252 | proto.toString = function Concurrently$toString() {
253 | if (!isOuter (this)) {
254 | invalidContext (OUTERNAME + '#toString', this, OUTERNAME);
255 | }
256 | return this[$$show] ();
257 | };
258 |
259 | return construct;
260 |
261 | };
262 |
263 | }));
264 |
265 | //. [FL3]: https://github.com/fantasyland/fantasy-land/
266 | //. [FL:Monad]: https://github.com/fantasyland/fantasy-land/#monad
267 | //. [FL:Alternative]: https://github.com/fantasyland/fantasy-land/#alternative
268 | //. [STI]: https://github.com/sanctuary-js/sanctuary-type-identifiers
269 | //. [SS]: https://github.com/sanctuary-js/sanctuary-show
270 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "concurrify",
3 | "version": "2.0.0",
4 | "description": "Turn non-concurrent FantasyLand Applicatives concurrent",
5 | "main": "index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git://github.com/fluture-js/concurrify.git"
9 | },
10 | "scripts": {
11 | "doctest": "sanctuary-doctest",
12 | "lint": "sanctuary-lint",
13 | "release": "sanctuary-release",
14 | "test": "npm run lint && sanctuary-test && npm run doctest"
15 | },
16 | "author": "Aldwin Vlasblom (https://github.com/Avaq)",
17 | "homepage": "https://github.com/fluture-js/concurrify",
18 | "bugs": {
19 | "url": "https://github.com/fluture-js/concurrify/issues"
20 | },
21 | "license": "MIT",
22 | "engines": {
23 | "node": ">=6.0.0"
24 | },
25 | "keywords": [
26 | "algebraic",
27 | "async",
28 | "asynchronous",
29 | "browser",
30 | "control-flow",
31 | "fantasy-land",
32 | "fp",
33 | "functional",
34 | "functor",
35 | "concurrent",
36 | "library",
37 | "monad",
38 | "monadic",
39 | "node",
40 | "parallel"
41 | ],
42 | "files": [
43 | "/index.js",
44 | "/LICENSE",
45 | "/package.json",
46 | "/README.md"
47 | ],
48 | "dependencies": {
49 | "sanctuary-show": "^1.0.0",
50 | "sanctuary-type-identifiers": "^2.0.0"
51 | },
52 | "devDependencies": {
53 | "chai": "^4.1.2",
54 | "codecov": "^3.0.0",
55 | "fantasy-land": "^4.0.1",
56 | "sanctuary-scripts": "^3.0.0",
57 | "sanctuary-type-classes": "^12.0.0"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/test/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | "mocha": true,
5 | "browser": false
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/test/concurrify.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var expect = require ('chai').expect;
4 | var FL = require ('fantasy-land');
5 | var show = require ('sanctuary-show');
6 | var Z = require ('sanctuary-type-classes');
7 | var type = require ('sanctuary-type-identifiers');
8 | var concurrify = require ('../');
9 |
10 | var $$type = '@@type';
11 |
12 | function Identity(x) {
13 | var id = {x: x, constructor: Identity};
14 | id[FL.ap] = function(mf) { return Identity (mf.x (x)); };
15 | id[FL.map] = function(f) { return Identity (f (x)); };
16 | return id;
17 | }
18 |
19 | Identity[FL.of] = Identity;
20 | Identity[$$type] = 'my/Identity@1';
21 |
22 | var mockZero = Identity ('zero');
23 |
24 | function mockAlt(a, b) { return b; }
25 |
26 | function mockAp(mx, mf) { return mx[FL.ap] (mf); }
27 |
28 | describe ('concurrify', function() {
29 |
30 | function noop() {}
31 |
32 | it ('throws when the first argument is not an Applicative Repr', function() {
33 | ['', {}, noop, String, Boolean].forEach (function(x) {
34 | function f() { concurrify (x, mockZero, mockAlt, mockAp); }
35 | expect (f).to.throw (TypeError, /represent an Applicative/);
36 | });
37 | });
38 |
39 | it ('throws when the second argument is not represented by the first', function() {
40 | ['', {}, null, 0].forEach (function(x) {
41 | function f() { concurrify (Identity, x, mockAlt, mockAp); }
42 | expect (f).to.throw (TypeError, /Identity/);
43 | });
44 | });
45 |
46 | it ('throws when the third argument is not a function', function() {
47 | ['', {}, null, 0].forEach (function(x) {
48 | function f() { concurrify (Identity, mockZero, x, mockAp); }
49 | expect (f).to.throw (TypeError, /be a function/);
50 | });
51 | });
52 |
53 | it ('throws when the third argument is not binary', function() {
54 | [noop, function(a) { return a; }].forEach (function(x) {
55 | function f() { concurrify (Identity, mockZero, x, mockAp); }
56 | expect (f).to.throw (TypeError, /be binary/);
57 | });
58 | });
59 |
60 | it ('throws when the fourth argument is not a function', function() {
61 | ['', {}, null, 0].forEach (function(x) {
62 | function f() { concurrify (Identity, mockZero, mockAlt, x); }
63 | expect (f).to.throw (TypeError, /be a function/);
64 | });
65 | });
66 |
67 | it ('throws when the fourth argument is not binary', function() {
68 | [noop, function(a) { return a; }].forEach (function(x) {
69 | function f() { concurrify (Identity, mockZero, mockAlt, x); }
70 | expect (f).to.throw (TypeError, /be binary/);
71 | });
72 | });
73 |
74 | it ('returns a new TypeRepr when given valid input', function() {
75 | var actual = concurrify (Identity, mockZero, mockAlt, mockAp);
76 | expect (actual).to.be.a ('function');
77 | expect (actual).to.have.property ($$type);
78 | expect (actual).to.have.property (FL.of);
79 | });
80 |
81 | describe ('TypeRepr', function() {
82 |
83 | var ConcurrentIdentity = concurrify (Identity, mockZero, mockAlt, mockAp);
84 |
85 | it ('throws when the first argument is not represented by Identity', function() {
86 | ['', {}, noop, String, Boolean].forEach (function(x) {
87 | function f() { ConcurrentIdentity (x); }
88 | expect (f).to.throw (TypeError);
89 | });
90 | });
91 |
92 | it ('creates Alternatives which are instances of itself', function() {
93 | var actual = ConcurrentIdentity (Z.of (Identity, 1));
94 | expect (actual).to.satisfy (Z.Alternative.test);
95 | expect (actual).to.be.an.instanceof (ConcurrentIdentity);
96 | });
97 |
98 | it ('reports being a ConcurrentIdentity from the same vendor and vendor', function() {
99 | var m = ConcurrentIdentity (Z.of (Identity, 1));
100 | expect (type (m)).to.equal ('my/ConcurrentIdentity@1');
101 | });
102 |
103 | describe ('.' + FL.of, function() {
104 |
105 | it ('creates a ConcurrentIdentity of an Identity of the input', function() {
106 | var actual = ConcurrentIdentity[FL.of] ('hello');
107 | expect (actual).to.be.an.instanceof (ConcurrentIdentity);
108 | expect (actual.sequential.constructor).to.equal (Identity);
109 | expect (actual.sequential.x).to.equal ('hello');
110 | });
111 |
112 | });
113 |
114 | describe ('.' + FL.zero, function() {
115 |
116 | it ('creates a ConcurrentIdentity of the return value of zero', function() {
117 | var actual = ConcurrentIdentity[FL.zero] ();
118 | expect (actual).to.be.an.instanceof (ConcurrentIdentity);
119 | expect (actual.sequential.constructor).to.equal (Identity);
120 | expect (actual.sequential.x).to.equal ('zero');
121 | });
122 |
123 | });
124 |
125 | describe ('#' + FL.map, function() {
126 |
127 | it ('throws when invoked out of context', function() {
128 | var m = ConcurrentIdentity[FL.of] (1);
129 | ['', {}, noop, String, Boolean].forEach (function(x) {
130 | function f() { m[FL.map].call (x); }
131 | expect (f).to.throw (TypeError, /context/);
132 | });
133 | });
134 |
135 | it ('throws when called without a function', function() {
136 | var m = ConcurrentIdentity[FL.of] (1);
137 | ['', {}, null, 0].forEach (function(x) {
138 | function f() { m[FL.map] (x); }
139 | expect (f).to.throw (TypeError, /be a function/);
140 | });
141 | });
142 |
143 | it ('delegates to the inner map', function(done) {
144 | var id = Identity (1);
145 |
146 | id[FL.map] = function(f) {
147 | expect (f).to.equal (noop);
148 | expect (this).to.equal (id);
149 | done ();
150 | };
151 |
152 | var cid = ConcurrentIdentity (id);
153 | cid[FL.map] (noop);
154 | });
155 |
156 | it ('behaves like map', function() {
157 | var m = ConcurrentIdentity[FL.of] (1);
158 | var m1 = m[FL.map] (function(x) { return x + 1; });
159 | expect (m1.sequential.x).to.equal (2);
160 | });
161 |
162 | });
163 |
164 | describe ('#' + FL.ap, function() {
165 |
166 | it ('throws when invoked out of context', function() {
167 | var m = ConcurrentIdentity[FL.of] (1);
168 | ['', {}, noop, String, Boolean].forEach (function(x) {
169 | function f() { m[FL.ap].call (x); }
170 | expect (f).to.throw (TypeError, /context/);
171 | });
172 | });
173 |
174 | it ('throws when called without a ConcurrentIdentity', function() {
175 | var m = ConcurrentIdentity[FL.of] (1);
176 | ['', {}, null, 0, noop].forEach (function(x) {
177 | function f() { m[FL.ap] (x); }
178 | expect (f).to.throw (TypeError, /ConcurrentIdentity/);
179 | });
180 | });
181 |
182 | it ('delegates to the given ap', function(done) {
183 | var x = 1;
184 | function f(x) { return x; }
185 | var idx = Identity (x);
186 | var idf = Identity (f);
187 | function mockAp(a, b) {
188 | expect (a).to.equal (idx);
189 | expect (b).to.equal (idf);
190 | done ();
191 | }
192 | var ConcurrentIdentity = concurrify (Identity, mockZero, mockAlt, mockAp);
193 | var cidx = ConcurrentIdentity (idx);
194 | var cidf = ConcurrentIdentity (idf);
195 | cidx[FL.ap] (cidf);
196 | });
197 |
198 | });
199 |
200 | describe ('#' + FL.alt, function() {
201 |
202 | it ('throws when invoked out of context', function() {
203 | var m = ConcurrentIdentity[FL.of] (1);
204 | ['', {}, noop, String, Boolean].forEach (function(x) {
205 | function f() { m[FL.alt].call (x); }
206 | expect (f).to.throw (TypeError, /context/);
207 | });
208 | });
209 |
210 | it ('throws when called without a ConcurrentIdentity', function() {
211 | var m = ConcurrentIdentity[FL.of] (1);
212 | ['', {}, null, 0, noop].forEach (function(x) {
213 | function f() { m[FL.alt] (x); }
214 | expect (f).to.throw (TypeError, /ConcurrentIdentity/);
215 | });
216 | });
217 |
218 | it ('delegates to the given alt', function(done) {
219 | var x = 1;
220 | function f(x) { return x; }
221 | var idx = Identity (x);
222 | var idf = Identity (f);
223 | function mockAlt(a, b) {
224 | expect (a).to.equal (idx);
225 | expect (b).to.equal (idf);
226 | done ();
227 | }
228 | var ConcurrentIdentity = concurrify (Identity, mockZero, mockAlt, mockAp);
229 | var cidx = ConcurrentIdentity (idx);
230 | var cidf = ConcurrentIdentity (idf);
231 | cidx[FL.alt] (cidf);
232 | });
233 |
234 | });
235 |
236 | describe ('#@@show', function() {
237 |
238 | var inner = Z.of (Identity, 1);
239 | var m = ConcurrentIdentity (inner);
240 |
241 | it ('returns a string representation of the data-structure', function() {
242 | expect (show (m)).to.equal ('ConcurrentIdentity(' + show (inner) + ')');
243 | });
244 |
245 | });
246 |
247 | describe ('#toString', function() {
248 |
249 | var inner = Z.of (Identity, 1);
250 | var m = ConcurrentIdentity (inner);
251 |
252 | it ('throws when invoked out of context', function() {
253 | ['', {}, noop, String, Boolean].forEach (function(x) {
254 | function f() { m.toString.call (x); }
255 | expect (f).to.throw (TypeError, /context/);
256 | });
257 | });
258 |
259 | it ('returns a string representation of the data-structure', function() {
260 | expect (m.toString ()).to.equal (show (m));
261 | });
262 |
263 | });
264 |
265 | });
266 |
267 | });
268 |
--------------------------------------------------------------------------------