├── .circleci
└── config.yml
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .npmignore
├── .nvmrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bin
├── build
├── lint
├── publish
├── tdd
└── test
├── docs
├── ADT.md
├── Core.md
├── Either.md
├── IO.md
├── Maybe.md
├── Reader.md
├── RemoteData.md
├── Result.md
├── Task.md
├── Tuple.md
├── Validation.md
└── Writer.md
├── package-lock.json
├── package.json
├── src
├── _tools.js
├── adt.js
├── core.js
├── either.js
├── index.js
├── io.js
├── maybe.js
├── reader.js
├── remote-data.js
├── result.js
├── task.js
├── tuple.js
├── validation.js
└── writer.js
└── test
├── _fantasy
└── interfaces
│ ├── either.test.js
│ ├── io.test.js
│ ├── maybe.test.js
│ ├── reader.test.js
│ ├── readerT.test.js
│ ├── remote-data.test.js
│ ├── result.test.js
│ ├── task.test.js
│ ├── tuple.test.js
│ ├── validation.test.js
│ └── writer.test.js
├── adt
├── tag.test.js
└── union.test.js
├── core
├── andThen.test.js
├── caseOf.test.js
├── cata.test.js
├── composeC.test.js
├── liftA2.test.js
├── liftA3.test.js
├── liftA4.test.js
└── pipeC.test.js
├── either
├── andThen.test.js
├── ap.test.js
├── caseOf.test.js
├── cata.test.js
├── chain.test.js
├── isLeft.test.js
├── isRight.test.js
├── map.test.js
├── of.test.js
├── toMaybe.test.js
├── toResult.test.js
├── toTask.test.js
├── toValidation.test.js
└── try.test.js
├── io
├── andThen.test.js
├── ap.test.js
├── chain.test.js
├── map.test.js
└── of.test.js
├── maybe
├── andThen.test.js
├── ap.test.js
├── caseOf.test.js
├── cata.test.js
├── chain.test.js
├── fromNullable.test.js
├── isJust.test.js
├── isNothing.test.js
├── map.test.js
├── of.test.js
├── toEither.test.js
├── toResult.test.js
├── toTask.test.js
├── toValidation.test.js
└── withDefault.test.js
├── reader
├── ap.test.js
├── chain.test.js
├── map.test.js
└── of.test.js
├── readerT
├── andThen.test.js
├── ap.test.js
├── chain.test.js
├── map.test.js
└── of.test.js
├── remote-data
├── andThen.test.js
├── ap.test.js
├── caseOf.test.js
├── cata.test.js
├── chain.test.js
├── concat.test.js
├── isFailure.test.js
├── isLoading.test.js
├── isNotAsked.test.js
├── isSuccess.test.js
├── map.test.js
├── of.test.js
└── withDefault.test.js
├── result
├── andThen.test.js
├── ap.test.js
├── caseOf.test.js
├── cata.test.js
├── chain.test.js
├── isErr.test.js
├── isOk.test.js
├── map.test.js
├── of.test.js
├── toEither.test.js
├── toMaybe.test.js
├── toTask.test.js
└── toValidation.test.js
├── task
├── andThen.test.js
├── ap.test.js
├── chain.test.js
├── fork.test.js
├── lift.test.js
├── liftNode.test.js
├── liftPromise.test.js
├── map.test.js
├── of.test.js
├── parallel.test.js
├── recover.test.js
├── reject.test.js
└── toPromise.test.js
├── tuple
├── equals.test.js
├── fst.test.js
├── map.test.js
├── mapLeft.test.js
├── snd.test.js
└── toString.test.js
├── validation
├── andThen.test.js
├── ap.test.js
├── caseOf.test.js
├── cata.test.js
├── chain.test.js
├── combine.test.js
├── concat.test.js
├── empty.test.js
├── isFailure.test.js
├── isSuccess.test.js
├── map.test.js
├── of.test.js
├── toEither.test.js
├── toMaybe.test.js
├── toResult.test.js
└── toTask.test.js
└── writer
├── andThen.test.js
├── ap.test.js
├── chain.test.js
├── map.test.js
├── of.test.js
└── tell.test.js
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 | orbs:
3 | node: circleci/node@1.1.6
4 | jobs:
5 | build-and-test:
6 | executor:
7 | name: node/default
8 | steps:
9 | - checkout
10 | - node/with-cache:
11 | steps:
12 | - run: npm install
13 | - run: npm run lint
14 | - run: npm test
15 | - run: npm run coverage
16 | workflows:
17 | build-and-test:
18 | jobs:
19 | - build-and-test
20 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/
2 | docs/
3 | coverage/
4 | node_modules/
5 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb-base",
3 | "rules": {
4 | "curly": 0,
5 | "no-multi-assign": 0,
6 | "no-use-before-define": 0,
7 | "no-underscore-dangle": 0
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | build
61 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .git
2 | coverage
3 | dist
4 | docs
5 | src
6 |
7 | .babelrc
8 | .eslintignore
9 | .eslintrc.json
10 | .gitignore
11 | .npmignore
12 | .nvmrc
13 | bower.json
14 | circle.yml
15 | webpack.config.js
16 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v12.16.2
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # [3.3.0](https://github.com/dustinws/zoom/compare/v3.2.0...v3.3.0) (2020-04-22)
3 |
4 |
5 | ### Features
6 |
7 | * **Core:** add pipeC and composeC helpers ([8c7d30b](https://github.com/dustinws/zoom/commit/8c7d30b))
8 |
9 |
10 |
11 |
12 | # [3.2.0](https://github.com/dustinws/zoom/compare/v3.1.0...v3.2.0) (2020-04-21)
13 |
14 |
15 | ### Features
16 |
17 | * **Validation:** add Validation.combine ([6ba0f40](https://github.com/dustinws/zoom/commit/6ba0f40))
18 |
19 |
20 |
21 |
22 | ## [3.0.4](https://github.com/dustinws/zoom/compare/v3.0.0...v3.0.4) (2020-04-21)
23 |
24 |
25 | ### Bug Fixes
26 |
27 | * **Reader:** Fix #andThen being assigned to itself ([5dfbf40](https://github.com/dustinws/zoom/commit/5dfbf40))
28 |
29 |
30 | ### Features
31 |
32 | * **Either:** add "toMaybe", "toResult", "toTask" ([6c3a8e3](https://github.com/dustinws/zoom/commit/6c3a8e3))
33 | * **Either:** add toValidation ([226a748](https://github.com/dustinws/zoom/commit/226a748))
34 | * **Maybe:** add toEither, toResult, toTask ([8168ecd](https://github.com/dustinws/zoom/commit/8168ecd))
35 | * **Maybe:** add toValidation ([44c5e7e](https://github.com/dustinws/zoom/commit/44c5e7e))
36 | * **Result:** add conversions to other types ([90715e9](https://github.com/dustinws/zoom/commit/90715e9))
37 | * **Validation:** add conversions to other types ([f65a33a](https://github.com/dustinws/zoom/commit/f65a33a))
38 |
39 |
40 |
41 |
42 | ## [3.0.4](https://github.com/dustinws/zoom/compare/v3.0.0...v3.0.4) (2020-04-19)
43 |
44 |
45 |
46 |
47 | # [3.0.0](https://github.com/dustinws/zoom/compare/v3.0.0-beta...v3.0.0) (2017-08-12)
48 |
49 |
50 | ### Features
51 |
52 | * **core:** add core utilities ([69ff5f6](https://github.com/dustinws/zoom/commit/69ff5f6))
53 |
54 |
55 |
56 |
57 | # [3.0.0-beta](https://github.com/dustinws/zoom/compare/v2.0.0...v3.0.0-beta) (2017-08-11)
58 |
59 |
60 |
61 |
62 | # [2.0.0](https://github.com/dustinws/zoom/compare/v1.1.0-beta...v2.0.0) (2017-07-15)
63 |
64 |
65 |
66 |
67 | # [1.1.0-beta](https://github.com/dustinws/zoom/compare/v1.0.0-beta...v1.1.0-beta) (2017-07-06)
68 |
69 |
70 |
71 |
72 | # 1.0.0-beta (2017-07-04)
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Dustin Stiles
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Zoom . js
2 |
3 | [](https://circleci.com/gh/dustinws/zoom/tree/master)
4 | [](https://badge.fury.io/js/zoomjs)
5 | [](https://david-dm.org/dustinws/zoom.svg)
6 | [](https://coveralls.io/github/dustinws/zoom?branch=master)
7 |
8 | ---
9 | ### Overview
10 |
11 | Zoom is a collection of tools to help javascript developers write safe, reliable
12 | code that is easy to read and easy to refactor.
13 |
14 | ---
15 | #### Helpers
16 | - [ADT](https://github.com/dustinws/zoom/blob/master/docs/ADT.md)
17 | - [Core](https://github.com/dustinws/zoom/blob/master/docs/Core.md)
18 |
19 | #### Types
20 | - [Either](https://github.com/dustinws/zoom/blob/master/docs/Either.md)
21 | - [IO](https://github.com/dustinws/zoom/blob/master/docs/IO.md)
22 | - [Maybe](https://github.com/dustinws/zoom/blob/master/docs/Maybe.md)
23 | - [Reader](https://github.com/dustinws/zoom/blob/master/docs/Reader.md)
24 | - [RemoteData](https://github.com/dustinws/zoom/blob/master/docs/RemoteData.md)
25 | - [Result](https://github.com/dustinws/zoom/blob/master/docs/Result.md)
26 | - [Task](https://github.com/dustinws/zoom/blob/master/docs/Task.md)
27 | - [Tuple](https://github.com/dustinws/zoom/blob/master/docs/Tuple.md)
28 | - [Validation](https://github.com/dustinws/zoom/blob/master/docs/Validation.md)
29 | - [Writer](https://github.com/dustinws/zoom/blob/master/docs/Writer.md)
30 |
31 | ---
32 |
33 | ###### Bundler Friendly
34 | Use direct require / import statements to only require the code that you need.
35 |
36 | ---
37 |
38 | ### Installation
39 |
40 | ##### npm
41 | `$ npm install --save zoomjs`
42 |
--------------------------------------------------------------------------------
/bin/build:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Clean existing build files
4 | if [ -d build ]; then
5 | echo "Removing 'build' files..."
6 | rm -rf build
7 | echo "Removed 'build' files."
8 | fi
9 |
10 | # Build source files with babel.
11 | echo "Building source files..."
12 | mkdir build
13 | cp -r src/ build
14 | echo "Source files built successfully."
15 |
16 | # Copy essential files into .tmp/deploy
17 | echo "Copying package.json..."
18 | cp package.json build
19 |
20 | echo "Copying package-lock.json..."
21 | cp package-lock.json build
22 |
23 | echo "Copying LICENSE..."
24 | cp LICENSE build
25 |
26 | echo "Copying README.md..."
27 | cp README.md build
28 |
--------------------------------------------------------------------------------
/bin/lint:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Run eslint
4 | ./node_modules/.bin/eslint .
5 |
--------------------------------------------------------------------------------
/bin/publish:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # At this point, it's expected that the tests have been run and linting
4 | # has passed. We can assume it's safe to proceed with the script.
5 |
6 |
7 | # Build source files
8 | echo "Building source files..."
9 | bin/build:node
10 | echo "Source files built successfully."
11 |
12 | # Start the "npm publish" command
13 | cd build/node
14 | echo "Starting '$ npm publish'"
15 | npm publish
16 | echo "Published to npm"
17 |
--------------------------------------------------------------------------------
/bin/tdd:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Run tests with jest
4 | NODE_ENV=testing ./node_modules/.bin/jest --watch
5 |
--------------------------------------------------------------------------------
/bin/test:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Run tests with jest
4 | NODE_ENV=testing ./node_modules/.bin/jest --coverage
5 |
--------------------------------------------------------------------------------
/docs/ADT.md:
--------------------------------------------------------------------------------
1 | # ADT
2 |
3 | Helpers for creating abstract data types.
4 |
5 |
6 | ### Static
7 |
8 | ---
9 |
10 | #### symbol
11 | ```hs
12 | symbol :: Symbol
13 | ```
14 |
15 | The identifier used to get an object's type.
16 |
17 | ```JavaScript
18 | import { symbol } from 'zoomjs/adt';
19 |
20 | const obj = {
21 | [symbol]: 'MyType',
22 | };
23 | ```
24 |
25 | ---
26 |
27 | #### tag
28 | `tag(string, ...params)`
29 |
30 | Create a tagged abstract data type. Tags the object with the "ADT.symbol" value, and creates a "toString" method.
31 |
32 | ```JavaScript
33 | import { tag, symbol } from 'zoomjs/adt';
34 |
35 | const Point2D = tag('Point2D', 'x', 'y');
36 |
37 | const point = Point2D(10, 15);
38 |
39 | point.x // 10
40 | point.y // 15
41 |
42 | point instanceof Point2D // true
43 | point[symbol] // 'Point2D'
44 | point.toString() // 'Point2D(0, 0)'
45 | ```
46 |
47 | ---
48 |
49 | #### union
50 | `union(string, object)`
51 |
52 | Create a set of union types that all inherit from the returned parent type. Adds a ".cata" method that acts as a switch between the types. Instead of passing a type and a list of parameter names like in "ADT.tag", an object is passed where the keys are the child type names and the values are their associated parameter list. If a type has no params (an empty array), an instance will be eagerly created to act as a singleton.
53 |
54 | ```JavaScript
55 | import { union } from 'zoomjs/adt';
56 |
57 | const Maybe = union('Maybe', {
58 | Just: ['value'],
59 | Nothing: [],
60 | });
61 |
62 | Maybe.prototype.getOrElse = function withDefault(defaultValue) {
63 | return this.cata({
64 | Just(value) {
65 | return value;
66 | },
67 |
68 | Nothing() {
69 | return defaultValue;
70 | },
71 | });
72 | };
73 |
74 | const justFoo = Maybe.Just('foo');
75 | const nothing = Maybe.Nothing;
76 |
77 | justFoo instanceof Maybe // true
78 | justFoo instanceof Maybe.Just // true
79 |
80 | nothing instanceof Maybe // true
81 |
82 | justFoo.withDefault('bar'); // 'foo'
83 | nothing.withDefault('bar'); // 'bar'
84 | ```
85 |
--------------------------------------------------------------------------------
/docs/Core.md:
--------------------------------------------------------------------------------
1 | # Core
2 |
3 | ---
4 |
5 | ### Static
6 |
7 | ---
8 |
9 | #### liftA2
10 | ```hs
11 | liftA2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
12 | ```
13 |
14 | Call a regular function with two arguments with the values contained in two separate monads of the same type. Returns a monad of the same type with the result.
15 |
16 | ```JavaScript
17 | import { liftA2 } from 'zoomjs/core';
18 | import { Just, Nothing } from 'zoomjs/maybe';
19 |
20 | const add = (a, b) => a + b;
21 |
22 | liftA2(add, Nothing, Nothing); // Nothing
23 | liftA2(add, Just(1), Nothing); // Nothing
24 | liftA2(add, Nothing, Just(1)); // Nothing
25 |
26 | liftA2(add, Just(1), Just(1)); // Just(2)
27 | ```
28 |
29 | ---
30 |
31 | #### liftA3
32 | ```hs
33 | liftA3 :: Monad m => (a -> b -> c -> d) -> m a -> m b -> m c -> m d
34 | ```
35 |
36 | Call a regular function with three arguments with the values contained in three separate monads of the same type. Returns a monad of the same type with the result.
37 |
38 | ```JavaScript
39 | import { liftA3 } from 'zoomjs/core';
40 | import { Just, Nothing } from 'zoomjs/maybe';
41 |
42 | const add3 = (a, b, c) => a + b + c;
43 |
44 | liftA3(add3, Nothing, Nothing, Nothing); // Nothing
45 | liftA3(add3, Just(1), Nothing, Nothing); // Nothing
46 | liftA3(add3, Nothing, Just(1), Nothing); // Nothing
47 | liftA3(add3, Nothing, Nothing, Just(1)); // Nothing
48 |
49 | liftA3(add3, Just(1), Just(1), Just(1)); // Just(3)
50 | ```
51 |
52 | ---
53 |
54 | #### liftA4
55 | ```hs
56 | liftA4 :: Monad m => (a -> b -> c -> d -> e) -> m a -> m b -> m c -> m d -> m e
57 | ```
58 |
59 | Call a regular function with four arguments with the values contained in four separate monads of the same type. Returns a monad of the same type with the result.
60 |
61 | ```JavaScript
62 | import { liftA4 } from 'zoomjs/core';
63 | import { Just, Nothing } from 'zoomjs/maybe';
64 |
65 | const add4 = (a, b, c, d) => a + b + c + d;
66 |
67 | liftA4(add4, Nothing, Nothing, Nothing, Nothing); // Nothing
68 | liftA4(add4, Just(1), Nothing, Nothing, Nothing); // Nothing
69 | liftA4(add4, Nothing, Just(1), Nothing, Nothing); // Nothing
70 | liftA4(add4, Nothing, Nothing, Just(1), Nothing); // Nothing
71 | liftA4(add4, Nothing, Nothing, Nothing, Just(1)); // Nothing
72 |
73 | liftA4(add4, Just(1), Just(1), Just(1), Just(1)); // Just(4)
74 | ```
75 |
76 | ---
77 |
78 | #### composeC
79 | ```hs
80 | composeC :: Chain m => ((y -> m z), (x -> m y), …, (a -> m b)) -> (a -> m z)
81 | ```
82 |
83 | Create a right to left composition of functions that return the same type of `Chain`.
84 |
85 | ```JavaScript
86 | import { composeC } from 'zoomjs/core';
87 | import Task from 'zoomjs/task';
88 |
89 | const add = a => b => Task.of(a + b);
90 | const multiply = a => b => Task.of(a * b);
91 |
92 | const doStuff = composeC(multiply(10), add(5));
93 |
94 | doStuff(5) // Task(null, 100)
95 | ```
96 |
97 | ---
98 |
99 | #### pipeC
100 | ```hs
101 | pipeC :: Chain m => ((a -> m b), (b -> m c), …, (y -> m z)) -> (a -> m z)
102 | ```
103 |
104 | Create a left to right composition of functions that return the same type of `Chain`.
105 |
106 | ```JavaScript
107 | import { pipeC } from 'zoomjs/core';
108 | import Task from 'zoomjs/task';
109 |
110 | const add = a => b => Task.of(a + b);
111 | const multiply = a => b => Task.of(a * b);
112 |
113 | const doStuff = pipeC(add(5), multiply(10));
114 |
115 | doStuff(5) // Task(null, 100)
116 | ```
117 |
--------------------------------------------------------------------------------
/docs/IO.md:
--------------------------------------------------------------------------------
1 | # IO
2 |
3 | #### Fantasy Land Implementations
4 | `Applicative`, `Functor`, `Apply`, `Chain`, `Monad`
5 |
6 | ---
7 | `IO` is a way to run computations that cause external effects, such
8 | as a database write.
9 |
10 | ---
11 |
12 | ### Static
13 |
14 | ---
15 |
16 | #### of
17 | ```hs
18 | of :: a -> IO a
19 | ```
20 |
21 | Create a new io that will return the given value.
22 |
23 | ```JavaScript
24 | import IO from 'zoomjs/io';
25 |
26 | IO.of('foo');
27 | // IO(foo)
28 | ```
29 |
30 | ---
31 |
32 | #### chain
33 | ```hs
34 | chain :: (a -> IO b) -> IO a -> IO b
35 | ```
36 |
37 | Run multiple IOs in sequence. The function given to `.chain` must return a new `IO`.
38 |
39 | ```JavaScript
40 | import fs from 'fs';
41 | import { IO } from 'zoomjs';
42 |
43 | // readFile :: String -> IO String
44 | const readFile = file =>
45 | IO(() => fs.readFileSync(file, 'utf8'));
46 |
47 | // log :: String -> IO String
48 | const log = message =>
49 | IO(() => {
50 | console.log(message);
51 | return message;
52 | });
53 |
54 | IO.chain(log, readFile('package.json'));
55 | // IO('{ "name": "zoomjs", ... }')
56 | ```
57 |
58 | ---
59 |
60 | #### map
61 | ```hs
62 | map :: (a -> b) -> IO a -> IO b
63 | ```
64 |
65 | Apply a function to the value held by a `IO`. Returns a new `IO`.
66 |
67 | ```JavaScript
68 | import { IO } from 'zoomjs';
69 |
70 | const toUpper = string =>
71 | string.toUpperCase();
72 |
73 | IO.map(toUpper, IO.of('foo'));
74 | // => IO('FOO')
75 | ```
76 |
77 | ---
78 |
79 | #### ap
80 | ```hs
81 | ap :: Apply (a -> b) -> IO a -> IO b
82 | ```
83 |
84 | Apply a function contained in an `Apply` to the value held by a `IO`. Returns a new `IO`.
85 |
86 | ```JavaScript
87 | import { IO } from 'zoomjs';
88 |
89 | const toUpper = string =>
90 | string.toUpperCase();
91 |
92 | IO.ap(IO.of(toUpper), IO.of('foo'));
93 | // => IO('FOO')
94 | ```
95 |
96 | ---
97 |
98 | ### Instance
99 |
100 | ---
101 |
102 | #### chain
103 | ```hs
104 | chain :: IO a ~> (a -> IO b) -> IO b
105 | ```
106 |
107 | Run multiple IOs in sequence. The function given to `.chain` must return a new `IO`.
108 |
109 | ```JavaScript
110 | import fs from 'fs';
111 | import { IO } from 'zoomjs';
112 |
113 | // readFile :: String -> IO String
114 | const readFile = file =>
115 | IO(() => fs.readFileSync(file, 'utf8'));
116 |
117 | // log :: String -> IO String
118 | const log = message =>
119 | IO(() => {
120 | console.log(message);
121 | return message;
122 | });
123 |
124 | readFile('package.json').chain(log);
125 | // IO('{ "name": "zoomjs", ... }')
126 | ```
127 |
128 | ---
129 |
130 | #### map
131 | ```hs
132 | map :: IO a ~> (a -> b) -> IO b
133 | ```
134 |
135 | Apply a function to the value held by a `IO`. Returns a new `IO`.
136 |
137 | ```JavaScript
138 | import { IO } from 'zoomjs';
139 |
140 | const toUpper = string =>
141 | string.toUpperCase();
142 |
143 | IO.of('foo').map(toUpper);
144 | // => IO('FOO')
145 | ```
146 |
147 | ---
148 |
149 | #### ap
150 | ```hs
151 | ap :: IO a ~> Apply (a -> b) -> IO b
152 | ```
153 |
154 | Apply a function contained in an `Apply` to the value held by a `IO`. Returns a new `IO`.
155 |
156 | ```JavaScript
157 | import { IO } from 'zoomjs';
158 |
159 | const toUpper = string =>
160 | string.toUpperCase();
161 |
162 | IO.of('foo').ap(IO.of(toUpper));
163 | // => IO('FOO')
164 | ```
165 |
--------------------------------------------------------------------------------
/docs/Reader.md:
--------------------------------------------------------------------------------
1 | # Reader
2 |
3 | #### Fantasy Land Implementations
4 | `Applicative`, `Functor`, `Apply`, `Chain`, `Monad`
5 |
6 | A `Reader` is way to do dependency injection. This makes it a great tool
7 | for testing, since it is easy to mock stateful operations.
8 |
9 | ---
10 |
11 | ### Static
12 |
13 | ---
14 |
15 | #### of
16 | ```hs
17 | of :: a -> Reader e a
18 | ```
19 |
20 | Create a new reader that will return the given value.
21 |
22 | ```JavaScript
23 | import { Reader } from 'zoomjs';
24 |
25 | Reader.of('foo'); // Reader(null, foo)
26 | ```
27 |
28 | ---
29 |
30 | #### chain
31 | ```hs
32 | chain :: (a -> Reader e b) -> Reader e a -> Reader e b
33 | ```
34 |
35 | Run multiple readers in sequence. The function given to `.chain` must return a `Reader`.
36 |
37 | ```JavaScript
38 | import { Reader } from 'zoomjs';
39 |
40 | // query :: String -> Reader Env [DbRow]
41 | const query = sql =>
42 | Reader(env => env.db.query(sql));
43 |
44 | Reader.chain(query, Reader.of('Select from "users";'));
45 | // Reader(Env, [DbRow])
46 | ```
47 |
48 | ---
49 |
50 | #### map
51 | ```hs
52 | map :: (a -> b) -> Reader e a -> Reader e b
53 | ```
54 |
55 | Apply a function to the value held by a `Reader`. Returns a new `Reader`.
56 |
57 | ```JavaScript
58 | import { Reader } from 'zoomjs';
59 |
60 | const reader = Reader.of('foo');
61 |
62 | Reader.map(x => x.toUpperCase(), reader);
63 | // => Reader(null, FOO)
64 | ```
65 |
66 | ---
67 |
68 | #### ap
69 | ```hs
70 | ap :: Apply (a -> b) -> Reader e a -> Reader e b
71 | ```
72 |
73 | Apply a function to the value held by a `Reader`. Returns a new `Reader`.
74 |
75 | ```JavaScript
76 | import { Reader } from 'zoomjs';
77 |
78 | const reader = Reader.of('foo');
79 | const toUpper = Reader.of(x => x.toUpperCase());
80 |
81 | Reader.ap(toUpper, reader);
82 | // => Reader(null, FOO)
83 | ```
84 |
85 | ---
86 |
87 | ### Instance
88 |
89 | ---
90 |
91 | #### chain
92 | ```hs
93 | chain :: Reader e a ~> (a -> Reader e b) -> Reader e b
94 | ```
95 |
96 | Run multiple readers in sequence. The function given to `.chain` must return a `Reader`.
97 |
98 | ```JavaScript
99 | import { Reader } from 'zoomjs';
100 |
101 | // query :: String -> Reader Env [DbRow]
102 | const query = sql =>
103 | Reader(env => env.db.query(sql));
104 |
105 | Reader.of('Select from "users"').chain(query);
106 | // Reader(Env, [DbRow])
107 | ```
108 |
109 | ---
110 |
111 | #### map
112 | ```hs
113 | map :: Reader e a ~> (a -> b) -> Reader e b
114 | ```
115 |
116 | Apply a function to the value held by a `Reader`. Returns a new `Reader`.
117 |
118 | ```JavaScript
119 | import { Reader } from 'zoomjs';
120 |
121 | const reader = Reader.of('foo');
122 |
123 | reader.map(x => x.toUpperCase());
124 | // => Reader(null, FOO)
125 | ```
126 |
127 | ---
128 |
129 | #### ap
130 | ```hs
131 | ap :: Reader e a ~> Apply (a -> b) -> Reader e b
132 | ```
133 |
134 | Apply a function to the value held by a `Reader`. Returns a new `Reader`.
135 |
136 | ```JavaScript
137 | import { Reader } from 'zoomjs';
138 |
139 | const reader = Reader.of('foo');
140 | const toUpper = Reader.of(x => x.toUpperCase());
141 |
142 | reader.ap(toUpper);
143 | // => Reader(null, FOO)
144 | ```
145 |
--------------------------------------------------------------------------------
/docs/Tuple.md:
--------------------------------------------------------------------------------
1 | # Tuple
2 |
3 | A `Tuple` is like a list with a fixed number of elements. In this
4 | implementation, we limit the number of elements to two, making this
5 | more akin to a `Pair` type.
6 |
7 | ---
8 |
9 | `Tuple` supports the `Symbol.iterator` protocol, which means it supports
10 | `for of` loops, `...spread`, and `[a, b] = Tuple(0, 0)` destructuring.
11 | For more detailed usage, see `Tuple#Iterator`
12 |
13 | ---
14 | #### Fantasy Land Implementations
15 | `Setoid`, `Functor`
16 |
17 | ### Usage
18 |
19 | ```JavaScript
20 | import { Tuple } from 'zoomjs';
21 |
22 | // Create a Tuple instance
23 | // user :: (Int, String)
24 | const user = Tuple(1, 'Dustin');
25 |
26 | user.toString(); // (1, 'Dustin')
27 | ```
28 |
29 | ---
30 |
31 | ### Static
32 |
33 | ---
34 |
35 | #### fst
36 | ```hs
37 | fst :: (a, b) -> a
38 | ```
39 |
40 | Get the first element of a Tuple
41 |
42 | ```JavaScript
43 | import { Tuple } from 'zoomjs';
44 |
45 | Tuple.fst(Tuple(1, 'Jake')); // 1
46 | ```
47 |
48 | ---
49 |
50 | #### snd
51 | ```hs
52 | snd :: (a, b) -> b
53 | ```
54 |
55 | Get the second element of a Tuple
56 |
57 | ```JavaScript
58 | import { Tuple } from 'zoomjs';
59 |
60 | Tuple.snd(Tuple(1, 'Jake')); // 'Jake'
61 | ```
62 |
63 | ---
64 |
65 | #### equals
66 | ```hs
67 | equals :: (a, b) -> (a, b) -> Bool
68 | ```
69 |
70 | Determine if one tuple is the same as another. Both elements are checked with the `===` comparison operator.
71 |
72 | ```JavaScript
73 | import { Tuple } from 'zoomjs';
74 |
75 | const userA = Tuple('male', 'Dustin');
76 | const userB = Tuple('male', 'Dustin');
77 | const userC = Tuple('male', 'Jimmy');
78 |
79 | Tuple.equals(userA, userB);
80 | // => true
81 |
82 | Tuple.equals(userA, userC);
83 | // => false
84 |
85 | Tuple.equals(userB, userC);
86 | // => false
87 | ```
88 |
89 | ---
90 |
91 | #### map
92 | ```hs
93 | map :: (b -> c) -> (a, b) -> (a, c)
94 | ```
95 |
96 | Apply a function to the second element of a tuple and return a new, modified tuple.
97 |
98 | ```JavaScript
99 | import { Tuple } from 'zoomjs';
100 |
101 | // toUpper :: String -> String
102 | const toUpper = x => x.toUpperCase();
103 |
104 | Tuple.map(toUpper, Tuple(1, 'jake'));
105 | // => (1, 'JAKE')
106 | ```
107 |
108 | ---
109 |
110 | #### mapLeft
111 | ```hs
112 | mapLeft :: (a -> c) -> (a, b) -> (c, b)
113 | ```
114 |
115 | Apply a function to the first element of a tuple and return a new, modified tuple.
116 |
117 | ```JavaScript
118 | import { Tuple } from 'zoomjs';
119 |
120 | const user = Tuple(1, 'Jake');
121 |
122 | Tuple.mapLeft(n => n + 1, user); // (2, 'Jake')
123 | ```
124 |
125 | ---
126 |
127 | ### Instance
128 |
129 | ---
130 |
131 | #### fst
132 | ```hs
133 | fst :: (a, b) ~> c -> a
134 | ```
135 |
136 | Get the first element of a Tuple. Instance version of "Tuple.fst"
137 |
138 | ```JavaScript
139 | import { Tuple } from 'zoomjs';
140 |
141 | Tuple(1, 'Jake').fst(); // 1
142 | ```
143 |
144 | ---
145 |
146 | #### snd
147 | ```hs
148 | snd :: (a, b) ~> c -> b
149 | ```
150 |
151 | Get the second element of a Tuple. Instance version of "Tuple.snd"
152 |
153 | ```JavaScript
154 | import { Tuple } from 'zoomjs';
155 |
156 | Tuple(1, 'Jake').snd(); // 'Jake'
157 | ```
158 |
159 | ---
160 |
161 | #### equals
162 | ```hs
163 | equals :: (a, b) ~> (a, b) -> Bool
164 | ```
165 |
166 | Determine if one tuple is the same as another. Both elements are checked with the `===` comparison operator.
167 |
168 | ```JavaScript
169 | import { Tuple } from 'zoomjs';
170 |
171 | const userA = Tuple('male', 'Dustin');
172 | const userB = Tuple('male', 'Dustin');
173 | const userC = Tuple('male', 'Jimmy');
174 |
175 | userA.equals(userB);
176 | // => true
177 |
178 | userA.equals(userC);
179 | // => false
180 |
181 | userB.equals(userC);
182 | // => false
183 | ```
184 |
185 | ---
186 |
187 | #### map
188 | ```hs
189 | map :: (a, b) ~> (b -> c) -> (a, c)
190 | ```
191 |
192 | Apply a function to the second element of a tuple and return a new, modified tuple. Instance version of "Tuple.map"
193 |
194 | ```JavaScript
195 | import { Tuple } from 'zoomjs';
196 |
197 | const user = Tuple(1, 'Jake');
198 |
199 | user.map(x => x.toUpperCase()).toString() // (1, 'JAKE')
200 | ```
201 |
202 | ---
203 |
204 | #### mapLeft
205 | ```hs
206 | mapLeft :: (a, b) ~> (a -> c) -> (c, b)
207 | ```
208 |
209 | Apply a function to the first element of a tuple and return a new, modified tuple. Instance version of `Tuple.mapLeft`
210 |
211 | ```JavaScript
212 | import { Tuple } from 'zoomjs';
213 |
214 | const user = Tuple(1, 'Jake');
215 |
216 | user.mapLeft(n => n + 1).toString(); // (2, 'Jake')
217 | ```
218 |
219 | ---
220 |
221 | #### Tuple#Iterator
222 |
223 | Support the `Symbol.iterator` protocol.
224 |
225 | ```JavaScript
226 | import { Tuple } from 'zoomjs';
227 |
228 | // user :: (Int, String)
229 | const user = Tuple(1, 'Janet');
230 |
231 | // For-of loops
232 | for (let element of user) {
233 | console.log(element);
234 | }
235 |
236 | // 1
237 | // 'Janet'
238 |
239 |
240 | // Spread Operator
241 | // saveUser :: Int -> String -> DbResult
242 | function saveUser(id, username) {
243 | return Db.save(id, username);
244 | }
245 |
246 | saveUser(...user);
247 |
248 |
249 | // Destructuring
250 | const [id, username] = user;
251 |
252 | // saveUserT :: (Int, String) -> DbResult
253 | function saveUserT([id, username]) {
254 | return Db.save(id, username);
255 | }
256 |
257 | saveUserT(user);
258 | ```
259 |
--------------------------------------------------------------------------------
/docs/Writer.md:
--------------------------------------------------------------------------------
1 | # Writer
2 |
3 | #### Fantasy Land Implementations
4 | `Applicative`, `Functor`, `Apply`, `Chain`, `Monad`
5 |
6 | ---
7 |
8 | A `Writer` is way to attach metadata to function calls, such as
9 | logging.
10 |
11 | ---
12 |
13 | ### Static
14 |
15 | ---
16 |
17 | #### of
18 | ```hs
19 | of :: a -> Writer w a
20 | ```
21 |
22 | Create a new writer that will return the given value.
23 |
24 | ```JavaScript
25 | import { Writer } from 'zoomjs';
26 |
27 | Writer.of('foo'); // Writer(null, foo)
28 | ```
29 |
30 | ---
31 |
32 | #### tell
33 | ```hs
34 | tell :: w -> Writer w a -> Writer w a
35 | ```
36 |
37 | Attach a new piece of metadata to a writer.
38 |
39 | ```JavaScript
40 | import { Writer } from 'zoomjs';
41 |
42 | Writer.tell('A log!', Writer.of('foo'));
43 | // Writer(Tuple('foo', ['A log!']))
44 | ```
45 |
46 | ---
47 |
48 | #### chain
49 | ```hs
50 | chain :: (a -> Writer w b) -> Writer w a -> Writer w b
51 | ```
52 |
53 | Run multiple writers in sequence. The function given to `.chain` must return a `Writer`.
54 |
55 | ```JavaScript
56 | import { Tuple, Writer } from 'zoomjs';
57 |
58 | // half :: Number -> Writer [String] Number
59 | const half = n =>
60 | Writer(Tuple(n / 2, `I just halved ${n}.`));
61 |
62 | Writer.chain(half, Writer.of(10));
63 | // Writer(Tuple(5, ['I just halved 10.']))
64 | ```
65 |
66 | ---
67 |
68 | #### map
69 | ```hs
70 | map :: (a -> b) -> Writer w a -> Writer w b
71 | ```
72 |
73 | Apply a function to the value held by a `Writer`. Returns a new `Writer`.
74 |
75 | ```JavaScript
76 | import { Writer } from 'zoomjs';
77 |
78 | const writer = Writer.of('foo');
79 |
80 | Writer.map(x => x.toUpperCase(), writer);
81 | // => Writer(Tuple('FOO', []))
82 | ```
83 |
84 | ---
85 |
86 | #### ap
87 | ```hs
88 | ap :: Apply (a -> b) -> Writer w a -> Writer w b
89 | ```
90 |
91 | Apply a function in an `Apply` to the value held by a `Writer`. Returns a new `Writer`.
92 |
93 | ```JavaScript
94 | import { Writer } from 'zoomjs';
95 |
96 | const writer = Writer.of('foo');
97 | const toUpper = x =>
98 | Writer(Tuple(x => x.toUpperCase(), `I just uppercased ${x}.`));
99 |
100 | Writer.ap(toUpper, writer);
101 | // => Writer(Tuple('FOO', ['I just uppercased foo.']))
102 | ```
103 |
104 | ---
105 |
106 | ### Instance
107 |
108 | ---
109 |
110 | #### chain
111 | ```hs
112 | chain :: Writer w a ~> (a -> Writer w b) -> Writer w b
113 | ```
114 |
115 | Run multiple writers in sequence. The function given to `.chain` must return a `Writer`.
116 |
117 | ```JavaScript
118 | import { Writer } from 'zoomjs';
119 |
120 | // half :: Number -> Writer [String] Number
121 | const half = n =>
122 | Writer(Tuple(n / 2, `I just halved ${n}.`));
123 |
124 | Writer.of(10).chain(half);
125 | // Writer(Tuple(5, ['I just halved 10.']))
126 | ```
127 |
128 | ---
129 |
130 | #### tell
131 | ```hs
132 | tell :: Writer w a ~> w -> Writer w a
133 | ```
134 |
135 | Attach a new piece of metadata to a writer.
136 |
137 | ```JavaScript
138 | import { Writer } from 'zoomjs';
139 |
140 | Writer.of('foo').tell('A log!');
141 | // Writer(Tuple('foo', ['A log!']))
142 | ```
143 |
144 | ---
145 |
146 | #### map
147 | ```hs
148 | map :: Writer w a ~> (a -> b) -> Writer w b
149 | ```
150 |
151 | Apply a function to the value held by a `Writer`. Returns a new `Writer`.
152 |
153 | ```JavaScript
154 | import { Writer } from 'zoomjs';
155 |
156 | const writer = Writer.of('foo');
157 |
158 | writer.map(x => x.toUpperCase());
159 | // => Writer(Tuple('FOO', []))
160 | ```
161 |
162 | ---
163 |
164 | #### ap
165 | ```hs
166 | ap :: Writer w a ~> Apply (a -> b) -> Writer w b
167 | ```
168 |
169 | Apply a function in an `Apply` to the value held by a `Writer`. Returns a new `Writer`.
170 |
171 | ```JavaScript
172 | import { Writer } from 'zoomjs';
173 |
174 | const writer = Writer.of('foo');
175 | const toUpper = Writer.of(x => x.toUpperCase());
176 |
177 | reaer.ap(toUpper);
178 | // => Writer(Tuple('FOO', []))
179 | ```
180 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zoomjs",
3 | "version": "3.3.23",
4 | "description": "Helpful abstractions for functional programming in javascript.",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "bin/build",
8 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
9 | "coverage": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
10 | "lint": "bin/lint",
11 | "tdd": "bin/tdd",
12 | "test": "bin/test"
13 | },
14 | "author": "Dustin ",
15 | "license": "MIT",
16 | "devDependencies": {
17 | "conventional-changelog-cli": "^2.1.1",
18 | "coveralls": "^3.0.12",
19 | "eslint": "^8.21.0",
20 | "eslint-config-airbnb-base": "^11.2.0",
21 | "eslint-plugin-import": "^2.6.1",
22 | "jest": "^28.1.3"
23 | },
24 | "dependencies": {
25 | "fantasy-land": "^4.0.1"
26 | },
27 | "repository": {
28 | "type": "git",
29 | "url": "git+https://github.com/dustinws/zoom.git"
30 | },
31 | "bugs": {
32 | "url": "https://github.com/dustinws/zoom/issues"
33 | },
34 | "homepage": "https://github.com/dustinws/zoom#readme"
35 | }
--------------------------------------------------------------------------------
/src/_tools.js:
--------------------------------------------------------------------------------
1 | // A functional placeholder.
2 | const __ = { '@@functional/placeholder': true };
3 |
4 | // Determine if an object is the placeholder.
5 | // Don't rely on === so we can maintain compatibility with other libraries.
6 | const _isPlaceholder = value =>
7 | value['@@functional/placeholder'];
8 |
9 | // Determine if a list contains the placeholder.
10 | const _hasPlaceholder = list =>
11 | !!list.find(_isPlaceholder);
12 |
13 | // Create a curried function
14 | const curry = fn =>
15 | function curried(...a) {
16 | if (a.length >= fn.length && !_hasPlaceholder(a))
17 | return fn(...a);
18 |
19 | return (...b) =>
20 | // eslint-disable-next-line no-confusing-arrow
21 | curried(...a.map(x => _isPlaceholder(x) ? b.shift() : x).concat(b));
22 | };
23 |
24 | // Create a function that always returns the given value.
25 | const always = value => () => value;
26 |
27 | // Create a left to right composition.
28 | const pipe = (...fns) => initialValue =>
29 | fns.reduce((a, b) => b(a), initialValue);
30 |
31 | // Create a right to left composition.
32 | const compose = (...fns) =>
33 | pipe(...fns.reverse());
34 |
35 |
36 | module.exports = {
37 | __,
38 | always,
39 | curry,
40 | compose,
41 | pipe,
42 | };
43 |
--------------------------------------------------------------------------------
/src/adt.js:
--------------------------------------------------------------------------------
1 | // symbol :: Symbol
2 | const symbol = Symbol('ADT.tag');
3 |
4 |
5 | function tag(type, ...params) {
6 | // Store the constructor on a temporary object so that
7 | // the constructor name will correctly match the type.
8 | let tmp = {
9 | // eslint-disable-next-line object-shorthand, func-names, consistent-return
10 | [type]: function (...args) {
11 | // Call new here so the consumer doesn't have to.
12 | if (!(this instanceof Adt)) {
13 | return new Adt(...args);
14 | }
15 |
16 | // Set all instance variables.
17 | params.forEach((param, idx) => {
18 | this[param] = args[idx];
19 | });
20 | },
21 | };
22 |
23 | // Retrieve the constructor and send the temporary object to GC.
24 | const Adt = tmp[type];
25 | tmp = null;
26 |
27 | Adt.prototype[symbol] = type;
28 | Adt[symbol] = type;
29 |
30 | Adt.prototype.toString = function toString() {
31 | const paramsList = params
32 | .map(param => this[param])
33 | .join(', ')
34 | .trim();
35 |
36 | const paramDisplay = params.length ? `(${paramsList})` : '';
37 | return `${this[symbol]}${paramDisplay}`;
38 | };
39 |
40 | return Adt;
41 | }
42 |
43 | function union(parentType, childTypes) {
44 | // Create the parent type.
45 | const Parent = tag(parentType);
46 |
47 | Object.keys(childTypes).forEach((childType) => {
48 | // Get any params defined for the child.
49 | const params = childTypes[childType];
50 |
51 | // Tag the child object.
52 | const Child = tag(childType, ...params);
53 |
54 | // Inherit from the parent type.
55 | const ogProto = Child.prototype;
56 | Child.prototype = Object.create(Parent.prototype);
57 | Child.prototype.constructor = Child;
58 |
59 | Object.assign(Child.prototype, ogProto);
60 |
61 | // Add the cata method.
62 | Child.prototype.cata = function cata(cases) {
63 | return cases[childType](...params.map(p => this[p]));
64 | };
65 |
66 | // Attach the child to the parent object. Eagerly create an
67 | // instance if there are no instance variables.
68 | Parent[childType] = params.length ? Child : Child();
69 | });
70 |
71 | return Parent;
72 | }
73 |
74 |
75 | module.exports = {
76 | symbol,
77 | tag,
78 | union,
79 | };
80 |
--------------------------------------------------------------------------------
/src/core.js:
--------------------------------------------------------------------------------
1 | const { curry } = require('./_tools');
2 |
3 |
4 | // liftA2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
5 | const liftA2 = curry((callback, a1, a2) =>
6 | a1.chain(a =>
7 | a2.map(b =>
8 | callback(a, b))));
9 |
10 | // liftA3 :: Monad m => (a -> b -> c -> d) -> m a -> m b -> m c -> m d
11 | const liftA3 = curry((callback, a1, a2, a3) =>
12 | a1.chain(a =>
13 | a2.chain(b =>
14 | a3.map(c =>
15 | callback(a, b, c)))));
16 |
17 | // liftA3 :: Monad m => (a -> b -> c -> d -> e) -> m a -> m b -> m c -> m d -> m e
18 | const liftA4 = curry((callback, a1, a2, a3, a4) =>
19 | a1.chain(a =>
20 | a2.chain(b =>
21 | a3.chain(c =>
22 | a4.map(d =>
23 | callback(a, b, c, d))))));
24 |
25 | const cata = curry((arg, object) =>
26 | object.cata(arg));
27 |
28 | const caseOf = curry((arg, object) =>
29 | object.caseOf(arg));
30 |
31 | // andThen :: Chain m => (a -> m b) -> m a -> m b
32 | const andThen = curry((arg, object) =>
33 | object.andThen(arg));
34 |
35 | // pipeC :: Chain m => ((a -> m b), (b -> m c), …, (y -> m z)) -> (a -> m z)
36 | const pipeC = (...[first, ...rest]) => v =>
37 | rest.reduce((a, b) => a.chain(b), first(v));
38 |
39 | // composeC :: Chain m => ((y -> m z), (x -> m y), …, (a -> m b)) -> (a -> m z)
40 | const composeC = (...ms) =>
41 | pipeC(...ms.reverse());
42 |
43 |
44 | module.exports = {
45 | liftA2,
46 | liftA3,
47 | liftA4,
48 | cata,
49 | caseOf,
50 | andThen,
51 | pipeC,
52 | composeC,
53 | };
54 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | exports.ADT = require('./adt');
2 | exports.core = require('./core');
3 | exports.Either = require('./either');
4 | exports.IO = require('./io');
5 | exports.Maybe = require('./maybe');
6 | exports.Reader = require('./reader');
7 | exports.RemoteData = require('./remote-data');
8 | exports.Result = require('./result');
9 | exports.Task = require('./task');
10 | exports.Tuple = require('./tuple');
11 | exports.Validation = require('./validation');
12 | exports.Writer = require('./writer');
13 |
--------------------------------------------------------------------------------
/src/io.js:
--------------------------------------------------------------------------------
1 | const FL = require('fantasy-land');
2 |
3 | const { tag } = require('./adt');
4 | const { __, curry } = require('./_tools');
5 |
6 |
7 | const IO = tag('IO', 'run');
8 |
9 |
10 | /*
11 | |------------------------------------------------------------------------------
12 | | Static Members
13 | |------------------------------------------------------------------------------
14 | */
15 |
16 | // of :: a -> IO a
17 | IO.of = value =>
18 | IO(() => value);
19 |
20 | // chain :: (a -> IO b) -> IO a -> IO b
21 | IO.chain = curry((transform, io) =>
22 | IO(env => transform(io.run(env)).run(env)));
23 |
24 | // andThen :: (a -> IO b) -> IO a -> IO b
25 | IO.andThen = IO.chain;
26 |
27 | // map :: (a -> b) -> IO a -> IO b
28 | IO.map = curry((transform, io) =>
29 | IO.chain(x => IO.of(transform(x)), io));
30 |
31 | // ap :: Apply (a -> b) -> IO a -> IO b
32 | IO.ap = curry((apply, io) =>
33 | IO.chain(IO.map(__, io), apply));
34 |
35 |
36 | /*
37 | |------------------------------------------------------------------------------
38 | | Instance Members
39 | |------------------------------------------------------------------------------
40 | */
41 |
42 | // of :: IO a ~> b -> IO b
43 | IO.prototype.of = function of(value) {
44 | return IO.of(value);
45 | };
46 |
47 | // chain :: IO a ~> (a -> IO b) -> IO b
48 | IO.prototype.chain = function chain(transform) {
49 | return IO.chain(transform, this);
50 | };
51 |
52 | // andThen :: IO a ~> (a -> IO b) -> IO b
53 | IO.prototype.andThen = IO.prototype.chain;
54 |
55 | // map :: IO a ~> (a -> b) -> IO b
56 | IO.prototype.map = function map(transform) {
57 | return IO.map(transform, this);
58 | };
59 |
60 | // ap :: IO a ~> Apply (a -> b) -> IO b
61 | IO.prototype.ap = function ap(apply) {
62 | return IO.ap(apply, this);
63 | };
64 |
65 |
66 | /*
67 | |------------------------------------------------------------------------------
68 | | Fantasy Land
69 | |------------------------------------------------------------------------------
70 | */
71 |
72 | // IO Applicative
73 | IO[FL.of] = IO.of;
74 | IO.prototype[FL.of] = IO.prototype.of;
75 |
76 | // IO Chain
77 | IO[FL.chain] = IO.chain;
78 | IO.prototype[FL.chain] = IO.prototype.chain;
79 |
80 | // IO Functor
81 | IO[FL.map] = IO.map;
82 | IO.prototype[FL.map] = IO.prototype.map;
83 |
84 | // IO Apply
85 | IO[FL.ap] = IO.ap;
86 | IO.prototype[FL.ap] = IO.prototype.ap;
87 |
88 |
89 | module.exports = IO;
90 |
--------------------------------------------------------------------------------
/src/reader.js:
--------------------------------------------------------------------------------
1 | const FL = require('fantasy-land');
2 |
3 | const { tag, symbol } = require('./adt');
4 | const { __, curry, always, compose } = require('./_tools');
5 |
6 |
7 | const Reader = tag('Reader', 'run');
8 |
9 |
10 | /*
11 | |------------------------------------------------------------------------------
12 | | Static Members
13 | |------------------------------------------------------------------------------
14 | */
15 |
16 | // of :: a -> Reader e a
17 | Reader.of = value =>
18 | Reader(() => value);
19 |
20 | // chain :: (a -> Reader e b) -> Reader e a -> Reader e b
21 | Reader.chain = curry((transform, reader) =>
22 | Reader(env => transform(reader.run(env)).run(env)));
23 |
24 | // andThen :: (a -> Reader e b) -> Reader e a -> Reader e b
25 | Reader.andThen = Reader.chain;
26 |
27 | // map :: (a -> b) -> Reader e a -> Reader e b
28 | Reader.map = curry((transform, reader) =>
29 | Reader.chain(x => Reader.of(transform(x)), reader));
30 |
31 | // ap :: Apply (a -> b) -> Reader e a -> Reader e b
32 | Reader.ap = curry((apply, reader) =>
33 | Reader.chain(Reader.map(__, reader), apply));
34 |
35 |
36 | /*
37 | |------------------------------------------------------------------------------
38 | | Instance Members
39 | |------------------------------------------------------------------------------
40 | */
41 |
42 | // of :: reader e a ~> b -> Either f b
43 | Reader.prototype.of = function of(value) {
44 | return Reader.of(value);
45 | };
46 |
47 | // chain :: Reader e a ~> (a -> Reader e b) -> Reader e b
48 | Reader.prototype.chain = function chain(transform) {
49 | return Reader.chain(transform, this);
50 | };
51 |
52 | // andThen :: Reader e a ~> (a -> Reader e b) -> Reader e b
53 | Reader.prototype.andThen = Reader.prototype.chain;
54 |
55 | // map :: Reader e a ~> (a -> b) -> Reader e b
56 | Reader.prototype.map = function map(transform) {
57 | return Reader.map(transform, this);
58 | };
59 |
60 | // ap :: Reader e a ~> Apply (a -> b) -> Reader e b
61 | Reader.prototype.ap = function ap(apply) {
62 | return Reader.ap(apply, this);
63 | };
64 |
65 |
66 | /*
67 | |------------------------------------------------------------------------------
68 | | Monad Transformer
69 | |------------------------------------------------------------------------------
70 | */
71 |
72 | Reader.T = (M) => {
73 | const ReaderT = tag(`Reader[${M[symbol]}]`, 'run');
74 |
75 | // Static
76 | // ------
77 |
78 | // ask :: Monad m => ReaderT e m a
79 | ReaderT.ask = ReaderT(M.of);
80 |
81 | // lift :: Monad m => m a -> ReaderT e m a
82 | ReaderT.lift = compose(ReaderT, always);
83 |
84 | // of :: Monad m => a -> ReaderT e m a
85 | ReaderT.of = value =>
86 | ReaderT(() => M.of(value));
87 |
88 | // chain :: Monad m => (a -> ReaderT e m b) -> ReaderT e m a -> ReaderT e m b
89 | ReaderT.chain = curry((callback, readerT) =>
90 | ReaderT(e => readerT.run(e).chain(a => callback(a).run(e))));
91 |
92 | // andThen :: Monad m => (a -> ReaderT e m b) -> ReaderT e m a -> ReaderT e m b
93 | ReaderT.andThen = ReaderT.chain;
94 |
95 | // map :: Monad m => (a -> b) -> ReaderT e m a -> ReaderT e m b
96 | ReaderT.map = curry((callback, readerT) =>
97 | readerT.chain(x => ReaderT.of(callback(x))));
98 |
99 | // ap :: Monad m => Apply (a -> b) -> ReaderT e m a -> ReaderT e m b
100 | ReaderT.ap = curry((apply, readerT) =>
101 | ReaderT(e => readerT.run(e).ap(apply.run(e))));
102 |
103 |
104 | // Instance
105 | // --------
106 |
107 | // of :: Monad m => ReaderT e m a ~> b -> ReaderT f m b
108 | ReaderT.prototype.of = function of(value) {
109 | return ReaderT.of(value);
110 | };
111 |
112 | // chain :: Monad m => ReaderT e m a ~> (a -> ReaderT e m b) -> ReaderT e m b
113 | ReaderT.prototype.chain = function chain(callback) {
114 | return ReaderT.chain(callback, this);
115 | };
116 |
117 | // andThen :: Monad m => ReaderT e m a ~> (a -> ReaderT e m b) -> ReaderT e m b
118 | ReaderT.prototype.andThen = ReaderT.prototype.chain;
119 |
120 | // map :: Monad m => ReaderT e m a ~> (a -> b) -> ReaderT e m b
121 | ReaderT.prototype.map = function map(callback) {
122 | return ReaderT.map(callback, this);
123 | };
124 |
125 | // ap :: Monad m => ReaderT e m a ~> Apply (a -> b) -> ReaderT e m b
126 | ReaderT.prototype.ap = function ap(apply) {
127 | return ReaderT.ap(apply, this);
128 | };
129 |
130 |
131 | // Static Monad
132 | ReaderT[FL.of] = ReaderT.of;
133 | ReaderT[FL.chain] = ReaderT.chain;
134 | ReaderT[FL.map] = ReaderT.map;
135 | ReaderT[FL.ap] = ReaderT.ap;
136 |
137 | // Instance Monad
138 | ReaderT.prototype[FL.of] = ReaderT.prototype.of;
139 | ReaderT.prototype[FL.chain] = ReaderT.prototype.chain;
140 | ReaderT.prototype[FL.map] = ReaderT.prototype.map;
141 | ReaderT.prototype[FL.ap] = ReaderT.prototype.ap;
142 |
143 |
144 | return ReaderT;
145 | };
146 |
147 |
148 | /*
149 | |------------------------------------------------------------------------------
150 | | Fantasy Land
151 | |------------------------------------------------------------------------------
152 | */
153 |
154 | // Reader Applicative
155 | Reader[FL.of] = Reader.of;
156 | Reader.prototype[FL.of] = Reader.prototype.of;
157 |
158 | // Reader Chain
159 | Reader[FL.chain] = Reader.chain;
160 | Reader.prototype[FL.chain] = Reader.prototype.chain;
161 |
162 | // Reader Functor
163 | Reader[FL.map] = Reader.map;
164 | Reader.prototype[FL.map] = Reader.prototype.map;
165 |
166 | // Reader Apply
167 | Reader[FL.ap] = Reader.ap;
168 | Reader.prototype[FL.ap] = Reader.prototype.ap;
169 |
170 |
171 | module.exports = Reader;
172 |
--------------------------------------------------------------------------------
/src/result.js:
--------------------------------------------------------------------------------
1 | const FL = require('fantasy-land');
2 |
3 | const Task = require('./task');
4 | const Either = require('./either');
5 | const Maybe = require('./maybe');
6 | const Validation = require('./validation');
7 |
8 | const { union } = require('./adt');
9 | const { __, curry, always, compose } = require('./_tools');
10 |
11 |
12 | const Result = union('Result', {
13 | Ok: ['value'],
14 | Err: ['value'],
15 | });
16 |
17 | const Ok = Result.Ok;
18 | const Err = Result.Err;
19 |
20 | /*
21 | |------------------------------------------------------------------------------
22 | | Static Members
23 | |------------------------------------------------------------------------------
24 | */
25 |
26 | // of :: b -> Result a b
27 | Result.of = function of(value) {
28 | return Ok(value);
29 | };
30 |
31 | // of :: b -> Result a b
32 | Ok.of = function of(value) {
33 | return Ok(value);
34 | };
35 |
36 | // of :: a -> Result a b
37 | Err.of = function of(value) {
38 | return Err(value);
39 | };
40 |
41 | // chain :: (b -> Result a c) -> Result a b -> Result a c
42 | Result.chain = curry((transform, result) =>
43 | result.cata({
44 | Err: always(result),
45 | Ok: transform,
46 | }));
47 |
48 | // andThen :: (b -> Result a c) -> Result a b -> Result a c
49 | Result.andThen = Result.chain;
50 |
51 | // map :: (b -> c) -> Result a b -> Result a c
52 | Result.map = curry((transform, result) =>
53 | Result.chain(compose(Result.of, transform), result));
54 |
55 | // map :: Apply (b -> c) -> Result a b -> Result a c
56 | Result.ap = curry((left, right) =>
57 | Result.chain(Result.map(__, right), left));
58 |
59 | // isErr :: Result a b -> Bool
60 | Result.isErr = result => result instanceof Result.Err;
61 |
62 | // isOk :: Result a b -> Bool
63 | Result.isOk = result => result instanceof Result.Ok;
64 |
65 | // toMaybe :: Result a b -> Maybe b
66 | Result.toMaybe = result =>
67 | result.cata({
68 | Err: Maybe.Nothing.of,
69 | Ok: Maybe.Just.of,
70 | });
71 |
72 | // toEither :: Result a b -> Either a b
73 | Result.toEither = result =>
74 | result.cata({
75 | Err: Either.Left.of,
76 | Ok: Either.Right.of,
77 | });
78 |
79 | // toTask :: Result a b -> Task a b
80 | Result.toTask = result =>
81 | result.cata({
82 | Err: Task.reject,
83 | Ok: Task.of,
84 | });
85 |
86 | // toValidation :: Result a b -> Validation a b
87 | Result.toValidation = result =>
88 | result.cata({
89 | Err: Validation.Failure.of,
90 | Ok: Validation.Success.of,
91 | });
92 |
93 |
94 | /*
95 | |------------------------------------------------------------------------------
96 | | Instance Members
97 | |------------------------------------------------------------------------------
98 | */
99 |
100 | // caseOf :: Result a b ~> { Err: a -> c, Ok: b -> c } -> c
101 | Result.prototype.caseOf = function caseOf(cases) {
102 | return this.cata(cases);
103 | };
104 |
105 | // of :: Result a b ~> d -> Result c d
106 | Ok.prototype.of = function of(value) {
107 | return Ok.of(value);
108 | };
109 |
110 | // of :: Result a b ~> c -> Result c d
111 | Err.prototype.of = function of(value) {
112 | return Err.of(value);
113 | };
114 |
115 | // chain :: Result a b ~> (b -> Result a c) -> Result a c
116 | Result.prototype.chain = function chain(transform) {
117 | return Result.chain(transform, this);
118 | };
119 |
120 | // andThen :: Result a b ~> (b -> Result a c) -> Result a c
121 | Result.prototype.andThen = function andThen(transform) {
122 | return Result.chain(transform, this);
123 | };
124 |
125 | // map :: Result a b ~> (b -> c) -> Result a c
126 | Result.prototype.map = function map(transform) {
127 | return Result.map(transform, this);
128 | };
129 |
130 | // ap :: Result a b ~> Apply (b -> c) -> Result a c
131 | Result.prototype.ap = function ap(apply) {
132 | return Result.ap(apply, this);
133 | };
134 |
135 | // isErr :: Result a b ~> c -> Bool
136 | Result.prototype.isErr = function isErr() {
137 | return Result.isErr(this);
138 | };
139 |
140 | // isOk :: Result a b ~> c -> Bool
141 | Result.prototype.isOk = function isOk() {
142 | return Result.isOk(this);
143 | };
144 |
145 | // toMaybe :: Result a b ~> Maybe b
146 | Result.prototype.toMaybe = function toMaybe() {
147 | return Result.toMaybe(this);
148 | };
149 |
150 | // toEither :: Result a b ~> Either a b
151 | Result.prototype.toEither = function toEither() {
152 | return Result.toEither(this);
153 | };
154 |
155 | // toTask :: Result a b ~> Task a b
156 | Result.prototype.toTask = function toTask() {
157 | return Result.toTask(this);
158 | };
159 |
160 | // toValidation :: Result a b ~> Validation a b
161 | Result.prototype.toValidation = function toValidation() {
162 | return Result.toValidation(this);
163 | };
164 |
165 | /*
166 | |------------------------------------------------------------------------------
167 | | Fantasy Land
168 | |------------------------------------------------------------------------------
169 | */
170 |
171 | // Result Applicative
172 | Result[FL.of] = Result.of;
173 |
174 | // Result Chain
175 | Result[FL.chain] = Result.chain;
176 | Result.prototype[FL.chain] = Result.prototype.chain;
177 |
178 | // Result Functor
179 | Result[FL.map] = Result.map;
180 | Result.prototype[FL.map] = Result.prototype.map;
181 |
182 | // Result Apply
183 | Result[FL.ap] = Result.ap;
184 | Result.prototype[FL.ap] = Result.prototype.ap;
185 |
186 |
187 | // Ok Applicative
188 | Result.Ok[FL.of] = Result.Ok.of;
189 | Result.Ok.prototype[FL.of] = Result.Ok.prototype.of;
190 |
191 | // Err Applicative
192 | Result.Err[FL.of] = Result.Err.of;
193 | Result.Err.prototype[FL.of] = Result.Err.prototype.of;
194 |
195 |
196 | module.exports = Result;
197 |
--------------------------------------------------------------------------------
/src/task.js:
--------------------------------------------------------------------------------
1 | const FL = require('fantasy-land');
2 |
3 | const { tag } = require('./adt');
4 | const { curry } = require('./_tools');
5 |
6 |
7 | const Task = tag('Task', 'fork');
8 |
9 |
10 | /*
11 | |------------------------------------------------------------------------------
12 | | Static Members
13 | |------------------------------------------------------------------------------
14 | */
15 |
16 | // of :: b -> Task a b
17 | Task.of = value =>
18 | Task((_, resolve) => resolve(value));
19 |
20 | // of :: a -> Task a b
21 | Task.reject = value =>
22 | Task(reject => reject(value));
23 |
24 | // fork :: (a -> c) -> (b -> c) -> Task a b -> d
25 | Task.fork = curry((reject, resolve, task) =>
26 | task.fork(reject, resolve));
27 |
28 | // chain :: (b -> Task a c) -> Task a b -> Task a c
29 | Task.chain = curry((transform, task) =>
30 | Task((reject, resolve) =>
31 | task.fork(reject, value =>
32 | transform(value).fork(reject, resolve))));
33 |
34 | // andThen :: (b -> Task a c) -> Task a b -> Task a c
35 | Task.andThen = Task.chain;
36 |
37 | // map :: (b -> c) -> Task a b -> Task a c
38 | Task.map = curry((transform, task) =>
39 | Task.chain(x => Task.of(transform(x)), task));
40 |
41 | // ap :: Apply (b -> c) -> Task a b -> Task a c
42 | Task.ap = curry((apply, task) =>
43 | Task.chain(transform => task.map(transform), apply));
44 |
45 | // toPromise :: Task a b -> Promise a b
46 | Task.toPromise = (task, Promise = global.Promise) =>
47 | new Promise((resolve, reject) =>
48 | task.fork(reject, resolve));
49 |
50 | // recover :: (a -> Task d c) -> Task a b -> Task d c
51 | Task.recover = curry((transform, task) =>
52 | Task((reject, resolve) =>
53 | task.fork(error =>
54 | transform(error).fork(reject, resolve), resolve)));
55 |
56 | // parallel :: [Task a b] -> Task a [b]
57 | Task.parallel = tasks =>
58 | Task((reject, resolve) => {
59 | let remaining = tasks.length;
60 | let rejected = false;
61 | let rejectedError;
62 |
63 | // Create the results array.
64 | const results = Array(tasks.length);
65 |
66 | // Fork each task
67 | tasks.forEach((task, idx) => {
68 | task.fork((error) => {
69 | if (!rejected) {
70 | rejected = true;
71 | rejectedError = error;
72 | return reject(error);
73 | }
74 |
75 | return rejectedError;
76 | }, (value) => {
77 | // Decrement the pending count
78 | remaining -= 1;
79 |
80 | // Store the value in the results array at the same
81 | // index the task was in.
82 | results[idx] = value;
83 |
84 | // Resolve if all tasks are done.
85 | if (remaining === 0) resolve(results);
86 | });
87 | });
88 | });
89 |
90 | // lift :: (a -> b) -> (a -> Task b)
91 | Task.lift = func => (...args) =>
92 | Task((_, resolve) => resolve(func(...args)));
93 |
94 | // liftNode :: (a, (b, c) -> d) -> (a -> Task b c)
95 | Task.liftNode = func => (...args) =>
96 | Task((reject, resolve) =>
97 | func(...args, (error, data) => {
98 | if (error) {
99 | return reject(error);
100 | }
101 | return resolve(data);
102 | }));
103 |
104 | // liftPromise :: (a -> Promise b c) -> (a -> Task b c)
105 | Task.liftPromise = func => (...args) =>
106 | Task((reject, resolve) =>
107 | func(...args).then(resolve).catch(reject));
108 |
109 |
110 | /*
111 | |------------------------------------------------------------------------------
112 | | Instance Members
113 | |------------------------------------------------------------------------------
114 | */
115 |
116 | // of :: Task a b ~> c -> Task f c
117 | Task.prototype.of = function of(value) {
118 | return Task.of(value);
119 | };
120 |
121 | // chain :: Task a b ~> (b -> Task a c) -> Task a c
122 | Task.prototype.chain = function chain(transform) {
123 | return Task.chain(transform, this);
124 | };
125 |
126 | // andThen :: Task a b ~> (b -> Task a c) -> Task a c
127 | Task.prototype.andThen = Task.prototype.chain;
128 |
129 | // map :: Task a b ~> (b -> c) -> Task a c
130 | Task.prototype.map = function map(transform) {
131 | return Task.map(transform, this);
132 | };
133 |
134 | // ap :: Task a b ~> Apply (b -> c) -> Task a c
135 | Task.prototype.ap = function ap(apply) {
136 | return Task.ap(apply, this);
137 | };
138 |
139 | // toPromise :: Task a b -> c -> Promise b
140 | Task.prototype.toPromise = function toPromise(Promise = global.Promise) {
141 | return Task.toPromise(this, Promise);
142 | };
143 |
144 | // recover :: Task a b ~> (a -> Task d c) -> Task d c
145 | Task.prototype.recover = function recover(transform) {
146 | return Task.recover(transform, this);
147 | };
148 |
149 | /*
150 | |------------------------------------------------------------------------------
151 | | Fantasy Land
152 | |------------------------------------------------------------------------------
153 | */
154 |
155 | // Static Monad
156 | Task[FL.of] = Task.of;
157 | Task[FL.chain] = Task.chain;
158 | Task[FL.map] = Task.map;
159 | Task[FL.ap] = Task.ap;
160 |
161 | // Instance Monad
162 | Task.prototype[FL.of] = Task.prototype.of;
163 | Task.prototype[FL.chain] = Task.prototype.chain;
164 | Task.prototype[FL.map] = Task.prototype.map;
165 | Task.prototype[FL.ap] = Task.prototype.ap;
166 |
167 |
168 | module.exports = Task;
169 |
--------------------------------------------------------------------------------
/src/tuple.js:
--------------------------------------------------------------------------------
1 | const FL = require('fantasy-land');
2 |
3 | const { tag } = require('./adt');
4 | const { curry } = require('./_tools');
5 |
6 | const Tuple = tag('Tuple', 'left', 'right');
7 |
8 |
9 | /*
10 | |------------------------------------------------------------------------------
11 | | Static Members
12 | |------------------------------------------------------------------------------
13 | */
14 |
15 | // fst :: (a, b) -> a
16 | Tuple.fst = tuple => tuple.left;
17 |
18 | // snd :: (a, b) -> b
19 | Tuple.snd = tuple => tuple.right;
20 |
21 | // equals :: (a, b) -> (a, b) -> Bool
22 | Tuple.equals = curry((left, right) =>
23 | left.left === right.left && left.right === right.right);
24 |
25 | // map :: (b -> c) -> (a, b) -> (a, c)
26 | Tuple.map = curry((transform, tuple) =>
27 | Tuple(tuple.left, transform(tuple.right)));
28 |
29 | // mapLeft :: (a -> c) -> (a, b) -> (c, b)
30 | Tuple.mapLeft = curry((transform, tuple) =>
31 | Tuple(transform(tuple.left), tuple.right));
32 |
33 |
34 | /*
35 | |------------------------------------------------------------------------------
36 | | Instance Members
37 | |------------------------------------------------------------------------------
38 | */
39 |
40 | // fst :: (a, b) ~> c -> a
41 | Tuple.prototype.fst = function fst() {
42 | return Tuple.fst(this);
43 | };
44 |
45 | // fst :: (a, b) ~> c -> b
46 | Tuple.prototype.snd = function snd() {
47 | return Tuple.snd(this);
48 | };
49 |
50 | // equals :: (a, b) ~> (a, b) -> Bool
51 | Tuple.prototype.equals = function equals(tuple) {
52 | return Tuple.equals(tuple, this);
53 | };
54 |
55 | // map :: (a, b) ~> (b -> c) -> (a, c)
56 | Tuple.prototype.map = function map(transform) {
57 | return Tuple.map(transform, this);
58 | };
59 |
60 | // mapLeft :: (a, b) ~> (a -> c) -> (c, b)
61 | Tuple.prototype.mapLeft = function mapLeft(transform) {
62 | return Tuple.mapLeft(transform, this);
63 | };
64 |
65 | // toString :: (a, b) ~> c -> String
66 | Tuple.prototype.toString = function toString() {
67 | return `(${this.left.toString()}, ${this.right.toString()})`;
68 | };
69 |
70 | // Symbol.iterator :: (a, b) ~> c -> Iterator
71 | Tuple.prototype[Symbol.iterator] = function iterator() {
72 | const pending = ['left', 'right'];
73 | const tuple = this;
74 | return {
75 | next() {
76 | if (pending.length) {
77 | return { value: tuple[pending.shift()] };
78 | }
79 | return { done: true };
80 | },
81 | };
82 | };
83 |
84 |
85 | /*
86 | |------------------------------------------------------------------------------
87 | | Fantasy Land
88 | |------------------------------------------------------------------------------
89 | */
90 |
91 | // Tuple Applicative
92 | Tuple[FL.equals] = Tuple.equals;
93 | Tuple.prototype[FL.equals] = Tuple.prototype.equals;
94 |
95 | // Tuple Functor
96 | Tuple[FL.map] = Tuple.map;
97 | Tuple.prototype[FL.map] = Tuple.prototype.map;
98 |
99 |
100 | module.exports = Tuple;
101 |
--------------------------------------------------------------------------------
/src/writer.js:
--------------------------------------------------------------------------------
1 | const FL = require('fantasy-land');
2 |
3 | const Tuple = require('./tuple');
4 | const { tag } = require('./adt');
5 | const { __, curry } = require('./_tools');
6 |
7 |
8 | const Writer = tag('Writer', 'value');
9 |
10 |
11 | /*
12 | |------------------------------------------------------------------------------
13 | | Static Members
14 | |------------------------------------------------------------------------------
15 | */
16 |
17 | // of :: a -> Writer w a
18 | Writer.of = value =>
19 | Writer(Tuple(value, []));
20 |
21 | // tell :: w -> Writer w a -> Writer w a
22 | Writer.tell = curry((value, writer) =>
23 | Writer(Tuple(Tuple.fst(writer.value), Tuple.snd(writer.value).concat(value))));
24 |
25 | // chain :: (a -> Writer w b) -> Writer w a -> Writer w b
26 | Writer.chain = curry((transform, writer) => {
27 | const [value, currentLogs] = writer.value;
28 | const [newVal, newLogs] = transform(value).value;
29 |
30 | return Writer(Tuple(newVal, currentLogs.concat(newLogs)));
31 | });
32 |
33 | // andThen :: (a -> Writer w b) -> Writer w a -> Writer w b
34 | Writer.andThen = Writer.chain;
35 |
36 | // map :: (a -> b) -> Writer w a -> Writer w b
37 | Writer.map = curry((transform, writer) =>
38 | Writer.chain(x => Writer.of(transform(x)), writer));
39 |
40 | // ap :: Apply (a -> b) -> Writer w a -> Writer w b
41 | Writer.ap = curry((apply, writer) =>
42 | Writer.chain(Writer.map(__, writer), apply));
43 |
44 |
45 | /*
46 | |------------------------------------------------------------------------------
47 | | Instance Members
48 | |------------------------------------------------------------------------------
49 | */
50 |
51 | // of :: Writer w a ~> b -> Writer w b
52 | Writer.prototype.of = function of(value) {
53 | return Writer.of(value);
54 | };
55 |
56 | // chain :: Writer w a ~> (a -> Writer w b) -> Writer w b
57 | Writer.prototype.chain = function chain(transform) {
58 | return Writer.chain(transform, this);
59 | };
60 |
61 | // andThen :: Writer w a ~> (a -> Writer w b) -> Writer w b
62 | Writer.prototype.andThen = Writer.prototype.chain;
63 |
64 | // tell :: Writer w a ~> w -> Writer w a
65 | Writer.prototype.tell = function tell(log) {
66 | return Writer.tell(log, this);
67 | };
68 |
69 | // map :: Writer w a ~> (a -> b) -> Writer w b
70 | Writer.prototype.map = function map(transform) {
71 | return Writer.map(transform, this);
72 | };
73 |
74 | // ap :: Writer w a ~> Apply (a -> b) -> Writer w b
75 | Writer.prototype.ap = function ap(apply) {
76 | return Writer.ap(apply, this);
77 | };
78 |
79 | /*
80 | |------------------------------------------------------------------------------
81 | | Fantasy Land
82 | |------------------------------------------------------------------------------
83 | */
84 |
85 | // Static Monad
86 | Writer[FL.of] = Writer.of;
87 | Writer[FL.chain] = Writer.chain;
88 | Writer[FL.map] = Writer.map;
89 | Writer[FL.ap] = Writer.ap;
90 |
91 | // Instance Monad
92 | Writer.prototype[FL.of] = Writer.prototype.of;
93 | Writer.prototype[FL.chain] = Writer.prototype.chain;
94 | Writer.prototype[FL.map] = Writer.prototype.map;
95 | Writer.prototype[FL.ap] = Writer.prototype.ap;
96 |
97 |
98 | module.exports = Writer;
99 |
--------------------------------------------------------------------------------
/test/_fantasy/interfaces/either.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const FL = require('fantasy-land');
4 | const Either = require('../../../src/either');
5 |
6 | describe('Fantasy Check - Either', () => {
7 | describe('Either', () => {
8 | test('Static Monad', () => {
9 | expect(typeof Either[FL.map]).toBe('function');
10 | expect(typeof Either[FL.ap]).toBe('function');
11 | expect(typeof Either[FL.of]).toBe('function');
12 | expect(typeof Either[FL.chain]).toBe('function');
13 | });
14 | });
15 |
16 | describe('Either.Left', () => {
17 | test('Instance Monad', () => {
18 | expect(typeof Either.Left.prototype[FL.map]).toBe('function');
19 | expect(typeof Either.Left.prototype[FL.ap]).toBe('function');
20 | expect(typeof Either.Left.prototype[FL.of]).toBe('function');
21 | expect(typeof Either.Left.prototype[FL.chain]).toBe('function');
22 | });
23 | });
24 |
25 | describe('Either.Right', () => {
26 | test('Instance Monad', () => {
27 | expect(typeof Either.Right.prototype[FL.map]).toBe('function');
28 | expect(typeof Either.Right.prototype[FL.ap]).toBe('function');
29 | expect(typeof Either.Right.prototype[FL.of]).toBe('function');
30 | expect(typeof Either.Right.prototype[FL.chain]).toBe('function');
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/test/_fantasy/interfaces/io.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const FL = require('fantasy-land');
4 | const IO = require('../../../src/io');
5 |
6 | describe('Fantasy Check - IO', () => {
7 | test('Static Monad', () => {
8 | expect(typeof IO[FL.map]).toBe('function');
9 | expect(typeof IO[FL.ap]).toBe('function');
10 | expect(typeof IO[FL.of]).toBe('function');
11 | expect(typeof IO[FL.chain]).toBe('function');
12 | });
13 |
14 | test('Instance Monad', () => {
15 | expect(typeof IO.prototype[FL.map]).toBe('function');
16 | expect(typeof IO.prototype[FL.ap]).toBe('function');
17 | expect(typeof IO.prototype[FL.of]).toBe('function');
18 | expect(typeof IO.prototype[FL.chain]).toBe('function');
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/_fantasy/interfaces/maybe.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const FL = require('fantasy-land');
4 | const Maybe = require('../../../src/maybe');
5 |
6 | describe('Fantasy Check - Maybe', () => {
7 | describe('Maybe', () => {
8 | test('Static Monad', () => {
9 | expect(typeof Maybe[FL.map]).toBe('function');
10 | expect(typeof Maybe[FL.ap]).toBe('function');
11 | expect(typeof Maybe[FL.of]).toBe('function');
12 | expect(typeof Maybe[FL.chain]).toBe('function');
13 | });
14 | });
15 |
16 | describe('Maybe.Nothing', () => {
17 | test('Static Monad', () => {
18 | expect(typeof Maybe.Nothing[FL.map]).toBe('function');
19 | expect(typeof Maybe.Nothing[FL.ap]).toBe('function');
20 | expect(typeof Maybe.Nothing[FL.of]).toBe('function');
21 | expect(typeof Maybe.Nothing[FL.chain]).toBe('function');
22 | });
23 | });
24 |
25 | describe('Maybe.Just', () => {
26 | test('Instance Monad', () => {
27 | expect(typeof Maybe.Just.prototype[FL.map]).toBe('function');
28 | expect(typeof Maybe.Just.prototype[FL.ap]).toBe('function');
29 | expect(typeof Maybe.Just.prototype[FL.of]).toBe('function');
30 | expect(typeof Maybe.Just.prototype[FL.chain]).toBe('function');
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/test/_fantasy/interfaces/reader.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const FL = require('fantasy-land');
4 | const Reader = require('../../../src/reader');
5 |
6 | describe('Fantasy Check - Reader', () => {
7 | test('Static Monad', () => {
8 | expect(typeof Reader[FL.map]).toBe('function');
9 | expect(typeof Reader[FL.ap]).toBe('function');
10 | expect(typeof Reader[FL.of]).toBe('function');
11 | expect(typeof Reader[FL.chain]).toBe('function');
12 | });
13 |
14 | test('Instance Monad', () => {
15 | expect(typeof Reader.prototype[FL.map]).toBe('function');
16 | expect(typeof Reader.prototype[FL.ap]).toBe('function');
17 | expect(typeof Reader.prototype[FL.of]).toBe('function');
18 | expect(typeof Reader.prototype[FL.chain]).toBe('function');
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/_fantasy/interfaces/readerT.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const FL = require('fantasy-land');
4 | const Reader = require('../../../src/reader');
5 | const Maybe = require('../../../src/maybe');
6 |
7 | const ReaderT = Reader.T(Maybe);
8 |
9 | describe('Fantasy Check - ReaderT', () => {
10 | test('Static Monad', () => {
11 | expect(typeof ReaderT[FL.map]).toBe('function');
12 | expect(typeof ReaderT[FL.ap]).toBe('function');
13 | expect(typeof ReaderT[FL.of]).toBe('function');
14 | expect(typeof ReaderT[FL.chain]).toBe('function');
15 | });
16 |
17 | test('Instance Monad', () => {
18 | expect(typeof ReaderT.prototype[FL.map]).toBe('function');
19 | expect(typeof ReaderT.prototype[FL.ap]).toBe('function');
20 | expect(typeof ReaderT.prototype[FL.of]).toBe('function');
21 | expect(typeof ReaderT.prototype[FL.chain]).toBe('function');
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/_fantasy/interfaces/remote-data.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const FL = require('fantasy-land');
4 | const RemoteData = require('../../../src/remote-data');
5 |
6 | describe('Fantasy Check - RemoteData', () => {
7 | test('Static Monad', () => {
8 | expect(typeof RemoteData[FL.map]).toBe('function');
9 | expect(typeof RemoteData[FL.ap]).toBe('function');
10 | expect(typeof RemoteData[FL.of]).toBe('function');
11 | expect(typeof RemoteData[FL.chain]).toBe('function');
12 | });
13 |
14 | test('Instance Semigroup', () => {
15 | expect(typeof RemoteData[FL.concat]).toBe('function');
16 | });
17 |
18 | test('Instance Monad', () => {
19 | expect(typeof RemoteData.prototype[FL.map]).toBe('function');
20 | expect(typeof RemoteData.prototype[FL.ap]).toBe('function');
21 | expect(typeof RemoteData.prototype[FL.of]).toBe('function');
22 | expect(typeof RemoteData.prototype[FL.chain]).toBe('function');
23 | });
24 |
25 | test('Instance Semigroup', () => {
26 | expect(typeof RemoteData.prototype[FL.concat]).toBe('function');
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/test/_fantasy/interfaces/result.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const FL = require('fantasy-land');
4 | const Result = require('../../../src/result');
5 |
6 | describe('Fantasy Check - Result', () => {
7 | describe('Result', () => {
8 | test('Static Monad', () => {
9 | expect(typeof Result[FL.map]).toBe('function');
10 | expect(typeof Result[FL.ap]).toBe('function');
11 | expect(typeof Result[FL.of]).toBe('function');
12 | expect(typeof Result[FL.chain]).toBe('function');
13 | });
14 | });
15 |
16 | describe('Result.Err', () => {
17 | test('Instance Monad', () => {
18 | expect(typeof Result.Err.prototype[FL.map]).toBe('function');
19 | expect(typeof Result.Err.prototype[FL.ap]).toBe('function');
20 | expect(typeof Result.Err.prototype[FL.of]).toBe('function');
21 | expect(typeof Result.Err.prototype[FL.chain]).toBe('function');
22 | });
23 | });
24 |
25 | describe('Result.Ok', () => {
26 | test('Instance Monad', () => {
27 | expect(typeof Result.Ok.prototype[FL.map]).toBe('function');
28 | expect(typeof Result.Ok.prototype[FL.ap]).toBe('function');
29 | expect(typeof Result.Ok.prototype[FL.of]).toBe('function');
30 | expect(typeof Result.Ok.prototype[FL.chain]).toBe('function');
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/test/_fantasy/interfaces/task.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const FL = require('fantasy-land');
4 | const Task = require('../../../src/task');
5 |
6 | describe('Fantasy Check - Task', () => {
7 | test('Static Monad', () => {
8 | expect(typeof Task[FL.map]).toBe('function');
9 | expect(typeof Task[FL.ap]).toBe('function');
10 | expect(typeof Task[FL.of]).toBe('function');
11 | expect(typeof Task[FL.chain]).toBe('function');
12 | });
13 |
14 | test('Instance Monad', () => {
15 | expect(typeof Task.prototype[FL.map]).toBe('function');
16 | expect(typeof Task.prototype[FL.ap]).toBe('function');
17 | expect(typeof Task.prototype[FL.of]).toBe('function');
18 | expect(typeof Task.prototype[FL.chain]).toBe('function');
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/_fantasy/interfaces/tuple.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const FL = require('fantasy-land');
4 | const Tuple = require('../../../src/tuple');
5 |
6 | describe('Fantasy Check - Tuple', () => {
7 | test('Static Setoid', () => {
8 | expect(typeof Tuple[FL.equals]).toBe('function');
9 | });
10 |
11 | test('Static Functor', () => {
12 | expect(typeof Tuple[FL.map]).toBe('function');
13 | });
14 |
15 | test('Instance Setoid', () => {
16 | expect(typeof Tuple.prototype[FL.equals]).toBe('function');
17 | });
18 |
19 | test('Instance Functor', () => {
20 | expect(typeof Tuple.prototype[FL.map]).toBe('function');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/_fantasy/interfaces/validation.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const FL = require('fantasy-land');
4 | const Validation = require('../../../src/validation');
5 |
6 | describe('Fantasy Check - Validation', () => {
7 | describe('Validation', () => {
8 | test('Static Monad', () => {
9 | expect(typeof Validation[FL.map]).toBe('function');
10 | expect(typeof Validation[FL.ap]).toBe('function');
11 | expect(typeof Validation[FL.of]).toBe('function');
12 | expect(typeof Validation[FL.chain]).toBe('function');
13 | });
14 |
15 | test('Static Monoid', () => {
16 | expect(typeof Validation[FL.concat]).toBe('function');
17 | expect(typeof Validation[FL.empty]).toBe('function');
18 | });
19 | });
20 |
21 | describe('Validation.Failure', () => {
22 | test('Instance Monad', () => {
23 | expect(typeof Validation.Failure.prototype[FL.map]).toBe('function');
24 | expect(typeof Validation.Failure.prototype[FL.ap]).toBe('function');
25 | expect(typeof Validation.Failure.prototype[FL.of]).toBe('function');
26 | expect(typeof Validation.Failure.prototype[FL.chain]).toBe('function');
27 | });
28 |
29 | test('Instance Monoid', () => {
30 | expect(typeof Validation.Failure.prototype[FL.concat]).toBe('function');
31 | expect(typeof Validation.Failure.prototype[FL.empty]).toBe('function');
32 | });
33 | });
34 |
35 | describe('Validation.Success', () => {
36 | test('Instance Monad', () => {
37 | expect(typeof Validation.Success.prototype[FL.map]).toBe('function');
38 | expect(typeof Validation.Success.prototype[FL.ap]).toBe('function');
39 | expect(typeof Validation.Success.prototype[FL.of]).toBe('function');
40 | expect(typeof Validation.Success.prototype[FL.chain]).toBe('function');
41 | });
42 |
43 | test('Instance Monoid', () => {
44 | expect(typeof Validation.Success.prototype[FL.concat]).toBe('function');
45 | expect(typeof Validation.Success.prototype[FL.empty]).toBe('function');
46 | });
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/test/_fantasy/interfaces/writer.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const FL = require('fantasy-land');
4 | const Writer = require('../../../src/writer');
5 |
6 | describe('Fantasy Check - Writer', () => {
7 | test('Static Monad', () => {
8 | expect(typeof Writer[FL.map]).toBe('function');
9 | expect(typeof Writer[FL.ap]).toBe('function');
10 | expect(typeof Writer[FL.of]).toBe('function');
11 | expect(typeof Writer[FL.chain]).toBe('function');
12 | });
13 |
14 | test('Instance Monad', () => {
15 | expect(typeof Writer.prototype[FL.map]).toBe('function');
16 | expect(typeof Writer.prototype[FL.ap]).toBe('function');
17 | expect(typeof Writer.prototype[FL.of]).toBe('function');
18 | expect(typeof Writer.prototype[FL.chain]).toBe('function');
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/adt/tag.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 | const { tag } = require('../../src/adt');
3 |
4 | describe('Adt.tag', () => {
5 | test('It should return a constructor function', () => {
6 | const T = tag('T');
7 | expect(new T() instanceof T).toBe(true);
8 | });
9 |
10 | test('It should not require the "new" keyworkd', () => {
11 | const T = tag('T');
12 | expect(T() instanceof T).toBe(true);
13 | });
14 |
15 | test('It should create a custom toString method', () => {
16 | const Point2D = tag('Point2D', 'x', 'y');
17 | expect(Point2D(0, 0).toString()).toEqual('Point2D(0, 0)');
18 |
19 | const Singleton = tag('Singleton');
20 | expect(Singleton().toString()).toEqual('Singleton');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/adt/union.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 | const { union } = require('../../src/adt');
3 |
4 | describe('#union', () => {
5 | test('It should attach the case classes to the returned object', () => {
6 | const T = union('T', {
7 | SomeCase: [],
8 | });
9 |
10 | expect(T.SomeCase).toBeTruthy();
11 | });
12 |
13 | test('The case classes should be instances of the super class', () => {
14 | const T = union('T', {
15 | SomeCase: ['someValue'],
16 | });
17 |
18 | expect(T.SomeCase() instanceof T).toBeTruthy();
19 | });
20 |
21 | test('The case class should be eagerly created if it has no params', () => {
22 | const T = union('T', {
23 | SomeCase: [],
24 | });
25 |
26 | expect(T.SomeCase instanceof T).toBeTruthy();
27 | });
28 |
29 | test('The case class should have a cata method', () => {
30 | const T = union('T', {
31 | SomeCase: [],
32 | });
33 |
34 | expect(typeof T.SomeCase.cata).toBe('function');
35 | });
36 |
37 | test('The case class\' cata methods should pick the on named after the type and pass in any params', () => {
38 | const T = union('T', {
39 | SomeCase: ['a', 'b'],
40 | });
41 |
42 | return new Promise((resolve) => {
43 | T.SomeCase(1, 2).cata({
44 | SomeCase(a, b) {
45 | expect(a).toBe(1);
46 | expect(b).toBe(2);
47 | resolve();
48 | },
49 | });
50 | });
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/test/core/andThen.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test, jest */
2 |
3 | const { andThen } = require('../../src/core');
4 | const Maybe = require('../../src/maybe');
5 |
6 |
7 | describe('Core.andThen', () => {
8 | test('It should dynamically dispatch to the "andThen" function of the instance.', () => {
9 | const just = Maybe.Just(10);
10 | const double = jest.fn(x => Maybe.of(x * 2));
11 |
12 | const result = andThen(double, just);
13 |
14 | expect(double).toHaveBeenCalledWith(10);
15 | expect(result).toEqual(Maybe.of(20));
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/test/core/caseOf.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test, jest */
2 |
3 | const { caseOf } = require('../../src/core');
4 | const Maybe = require('../../src/maybe');
5 |
6 |
7 | describe('Core.caseOf', () => {
8 | test('It should dynamically dispatch to the "caseOf" function of the instance.', () => {
9 | const just = Maybe.Just(1);
10 |
11 | const onNothing = jest.fn();
12 | const onJust = jest.fn();
13 |
14 | caseOf({
15 | Nothing: onNothing,
16 | Just: onJust,
17 | }, just);
18 |
19 | expect(onJust).toHaveBeenCalledWith(1);
20 | expect(onNothing).not.toHaveBeenCalled();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/core/cata.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test, jest */
2 |
3 | const { cata } = require('../../src/core');
4 | const Maybe = require('../../src/maybe');
5 |
6 |
7 | describe('Core.cata', () => {
8 | test('It should dynamically dispatch to the "cata" function of the instance.', () => {
9 | const just = Maybe.Just(1);
10 |
11 | const onNothing = jest.fn();
12 | const onJust = jest.fn();
13 |
14 | cata({
15 | Nothing: onNothing,
16 | Just: onJust,
17 | }, just);
18 |
19 | expect(onJust).toHaveBeenCalledWith(1);
20 | expect(onNothing).not.toHaveBeenCalled();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/core/composeC.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test, jest */
2 |
3 | const { composeC } = require('../../src/core');
4 | const Task = require('../../src/task');
5 |
6 |
7 | describe('Core.composeC', () => {
8 | test('It should call each function in sequence and provide the return value of the last as the input to the next.', () => {
9 | const resolveHandler = jest.fn();
10 | const rejectHandler = jest.fn();
11 |
12 | const add = a => b => Task.of(a + b);
13 | const multiply = a => b => Task.of(a * b);
14 |
15 | const computation = composeC(multiply(10), add(5));
16 |
17 | computation(1).fork(rejectHandler, resolveHandler);
18 |
19 | expect(rejectHandler).not.toHaveBeenCalled();
20 | expect(resolveHandler).toHaveBeenCalledWith(60);
21 | });
22 |
23 | test('If the type has unions that short circuit chaining, that instance should be propagated.', () => {
24 | const resolveHandler = jest.fn();
25 | const rejectHandler = jest.fn();
26 |
27 | const error = Task.reject('Error');
28 | const errorCreator = () => error;
29 |
30 | const funcA = jest.fn(Task.of);
31 | const funcB = jest.fn(Task.of);
32 |
33 | const computation = composeC(funcA, funcB, errorCreator);
34 |
35 | computation(1).fork(rejectHandler, resolveHandler);
36 |
37 | expect(resolveHandler).not.toHaveBeenCalled();
38 | expect(rejectHandler).toHaveBeenCalledWith('Error');
39 |
40 | expect(funcA).not.toHaveBeenCalled();
41 | expect(funcB).not.toHaveBeenCalled();
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/core/liftA2.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const { liftA2 } = require('../../src/core');
4 | const { Just, Nothing } = require('../../src/maybe');
5 |
6 |
7 | describe('Core.liftA2', () => {
8 | test('It should extract the values from the monads and call the funciton with them.', () => {
9 | const add2 = (a, b) => a + b;
10 |
11 | expect(liftA2(add2, Nothing, Nothing)).toEqual(Nothing);
12 | expect(liftA2(add2, Just(1), Nothing)).toEqual(Nothing);
13 | expect(liftA2(add2, Nothing, Just(1))).toEqual(Nothing);
14 |
15 | expect(liftA2(add2, Just(1), Just(1))).toEqual(Just(2));
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/test/core/liftA3.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const { liftA3 } = require('../../src/core');
4 | const { Just, Nothing } = require('../../src/maybe');
5 |
6 |
7 | describe('Core.liftA3', () => {
8 | test('It should extract the values from the monads and call the funciton with them.', () => {
9 | const add3 = (a, b, c) => a + b + c;
10 |
11 | expect(liftA3(add3, Nothing, Nothing, Nothing)).toEqual(Nothing);
12 | expect(liftA3(add3, Just(1), Nothing, Nothing)).toEqual(Nothing);
13 | expect(liftA3(add3, Nothing, Just(1), Nothing)).toEqual(Nothing);
14 | expect(liftA3(add3, Nothing, Nothing, Just(1))).toEqual(Nothing);
15 |
16 | expect(liftA3(add3, Just(1), Just(1), Just(1))).toEqual(Just(3));
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/test/core/liftA4.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const { liftA4 } = require('../../src/core');
4 | const { Just, Nothing } = require('../../src/maybe');
5 |
6 |
7 | describe('Core.liftA4', () => {
8 | test('It should extract the values from the monads and call the funciton with them.', () => {
9 | const add4 = (a, b, c, d) => a + b + c + d;
10 |
11 | expect(liftA4(add4, Nothing, Nothing, Nothing, Nothing)).toEqual(Nothing);
12 | expect(liftA4(add4, Just(1), Nothing, Nothing, Nothing)).toEqual(Nothing);
13 | expect(liftA4(add4, Nothing, Just(1), Nothing, Nothing)).toEqual(Nothing);
14 | expect(liftA4(add4, Nothing, Nothing, Just(1), Nothing)).toEqual(Nothing);
15 | expect(liftA4(add4, Nothing, Nothing, Nothing, Just(1))).toEqual(Nothing);
16 |
17 | expect(liftA4(add4, Just(1), Just(1), Just(1), Just(1))).toEqual(Just(4));
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/core/pipeC.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test, jest */
2 |
3 | const { pipeC } = require('../../src/core');
4 | const Task = require('../../src/task');
5 |
6 |
7 | describe('Core.pipeC', () => {
8 | test('It should call each function in sequence and provide the return value of the last as the input to the next.', () => {
9 | const resolveHandler = jest.fn();
10 | const rejectHandler = jest.fn();
11 |
12 | const add = a => b => Task.of(a + b);
13 | const multiply = a => b => Task.of(a * b);
14 |
15 | const computation = pipeC(add(5), multiply(10));
16 |
17 | computation(1).fork(rejectHandler, resolveHandler);
18 |
19 | expect(rejectHandler).not.toHaveBeenCalled();
20 | expect(resolveHandler).toHaveBeenCalledWith(60);
21 | });
22 |
23 | test('If the type has unions that short circuit chaining, that instance should be propagated.', () => {
24 | const resolveHandler = jest.fn();
25 | const rejectHandler = jest.fn();
26 |
27 | const error = Task.reject('Error');
28 | const errorCreator = () => error;
29 |
30 | const funcA = jest.fn(Task.of);
31 | const funcB = jest.fn(Task.of);
32 |
33 | const computation = pipeC(errorCreator, funcA, funcB);
34 |
35 | computation(1).fork(rejectHandler, resolveHandler);
36 |
37 | expect(resolveHandler).not.toHaveBeenCalled();
38 | expect(rejectHandler).toHaveBeenCalledWith('Error');
39 |
40 | expect(funcA).not.toHaveBeenCalled();
41 | expect(funcB).not.toHaveBeenCalled();
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/either/andThen.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 | const { Left, Right } = Either;
6 |
7 | describe('Data.Either', () => {
8 | describe('Either.andThen', () => {
9 | test('It should apply the transform if the instance is a Right', () => {
10 | const transform = x => x.toUpperCase();
11 | const either = Right.of('text');
12 |
13 | const result = Either.andThen(transform, either);
14 |
15 | expect(result).toBe('TEXT');
16 | });
17 |
18 | test('It should ignore the transform if the instance is a Left', () => {
19 | const transform = x => x.toUpperCase();
20 | const either = Left.of('text');
21 |
22 | const result = Either.andThen(transform, either);
23 |
24 | expect(result).toBe(either);
25 | });
26 | });
27 |
28 | describe('Either#andThen', () => {
29 | test('It should apply the transform if the instance is a Right', () => {
30 | const result = Right.of('text');
31 | const value = result.andThen(x => x.toUpperCase());
32 |
33 | expect(value).toBe('TEXT');
34 | });
35 |
36 | test('It should ignore the transform if the instance is a Left', () => {
37 | const result = Left.of('text');
38 | const value = result.andThen(x => x.toUpperCase());
39 |
40 | expect(value).toBe(result);
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/either/ap.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 | const { Left, Right } = Either;
6 |
7 | describe('Data.Either', () => {
8 | describe('Either.ap', () => {
9 | test('It should apply the transform if the instance is a Right', () => {
10 | const either = Right.of('text');
11 | const apply = Right.of(x => x.toUpperCase());
12 |
13 | const result = Either.ap(apply, either);
14 |
15 | expect(Either.isRight(result)).toBe(true);
16 | expect(result.value).toEqual('TEXT');
17 | });
18 |
19 | test('It should ignore the transform if the instance is a Left', () => {
20 | const either = Left.of('text');
21 | const apply = Right.of(x => x.toUpperCase());
22 |
23 | const result = Either.ap(apply, either);
24 |
25 | expect(Either.isLeft(result)).toBe(true);
26 | expect(result.value).toEqual('text');
27 | });
28 | });
29 |
30 | describe('Either#ap', () => {
31 | test('It should apply the transform if the instance is a Right', () => {
32 | const result = Either
33 | .Right.of('text')
34 | .ap(Right.of(x => x.toUpperCase()));
35 |
36 | expect(Either.isRight(result)).toBe(true);
37 | expect(result.value).toBe('TEXT');
38 | });
39 |
40 | test('It should ignore the transform if the instance is a Left', () => {
41 | const result = Either
42 | .Left.of('text')
43 | .ap(Right.of(x => x.toUpperCase()));
44 |
45 | expect(Either.isLeft(result)).toBe(true);
46 | expect(result.value).toBe('text');
47 | });
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/test/either/caseOf.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 | const { Left, Right } = Either;
6 |
7 |
8 | describe('Data.Either', () => {
9 | describe('Left#caseOf', () => {
10 | test('It should call the "Left" function.', () => {
11 | const leftCB = jest.fn();
12 | const rightCB = jest.fn();
13 |
14 | Left().caseOf({
15 | Left: leftCB,
16 | Right: rightCB,
17 | });
18 |
19 | expect(leftCB.mock.calls.length).toBe(1);
20 | expect(rightCB.mock.calls.length).toBe(0);
21 | });
22 | });
23 |
24 | describe('Right#caseOf', () => {
25 | test('It should call the "Right" function.', () => {
26 | const leftCB = jest.fn();
27 | const rightCB = jest.fn();
28 |
29 | Right().caseOf({
30 | Left: leftCB,
31 | Right: rightCB,
32 | });
33 |
34 | expect(leftCB.mock.calls.length).toBe(0);
35 | expect(rightCB.mock.calls.length).toBe(1);
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/either/cata.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 | const { Left, Right } = Either;
6 |
7 |
8 | describe('Data.Either', () => {
9 | describe('Left#cata', () => {
10 | test('It should call the "Left" function.', () => {
11 | const leftCB = jest.fn();
12 | const rightCB = jest.fn();
13 |
14 | Left().cata({
15 | Left: leftCB,
16 | Right: rightCB,
17 | });
18 |
19 | expect(leftCB.mock.calls.length).toBe(1);
20 | expect(rightCB.mock.calls.length).toBe(0);
21 | });
22 | });
23 |
24 | describe('Right#cata', () => {
25 | test('It should call the "Right" function.', () => {
26 | const leftCB = jest.fn();
27 | const rightCB = jest.fn();
28 |
29 | Right().cata({
30 | Left: leftCB,
31 | Right: rightCB,
32 | });
33 |
34 | expect(leftCB.mock.calls.length).toBe(0);
35 | expect(rightCB.mock.calls.length).toBe(1);
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/either/chain.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 | const { Left, Right } = Either;
6 |
7 | describe('Data.Either', () => {
8 | describe('Either.chain', () => {
9 | test('It should apply the transform if the instance is a Right', () => {
10 | const transform = x => x.toUpperCase();
11 | const either = Right.of('text');
12 |
13 | const result = Either.chain(transform, either);
14 |
15 | expect(result).toBe('TEXT');
16 | });
17 |
18 | test('It should ignore the transform if the instance is a Left', () => {
19 | const transform = x => x.toUpperCase();
20 | const either = Left.of('text');
21 |
22 | const result = Either.chain(transform, either);
23 |
24 | expect(result).toBe(either);
25 | });
26 | });
27 |
28 | describe('Either#chain', () => {
29 | test('It should apply the transform if the instance is a Right', () => {
30 | const result = Right.of('text');
31 | const value = result.chain(x => x.toUpperCase());
32 |
33 | expect(value).toBe('TEXT');
34 | });
35 |
36 | test('It should ignore the transform if the instance is a Left', () => {
37 | const result = Left.of('text');
38 | const value = result.chain(x => x.toUpperCase());
39 |
40 | expect(value).toBe(result);
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/either/isLeft.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 | const { Left, Right } = Either;
6 |
7 | describe('Data.Either', () => {
8 | describe('Either.isLeft', () => {
9 | test('It should return true if the instance is a Left', () => {
10 | expect(Either.isLeft(Left.of())).toBe(true);
11 | });
12 |
13 | test('It should return false if the instance is a Right', () => {
14 | expect(Either.isLeft(Right.of())).toBe(false);
15 | });
16 | });
17 |
18 | describe('Either#isLeft', () => {
19 | test('It should return true if the instance is a Left', () => {
20 | expect(Left.of().isLeft()).toBe(true);
21 | });
22 |
23 | test('It should return false if the instance is a Right', () => {
24 | expect(Right.of().isLeft()).toBe(false);
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/test/either/isRight.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 | const { Left, Right } = Either;
6 |
7 | describe('Data.Either', () => {
8 | describe('Either.isRight', () => {
9 | test('It should return true if the instance is a Right', () => {
10 | expect(Either.isRight(Right.of())).toBe(true);
11 | });
12 |
13 | test('It should return false if the instance is a Left', () => {
14 | expect(Either.isRight(Left.of())).toBe(false);
15 | });
16 | });
17 |
18 | describe('Either#isRight', () => {
19 | test('It should return true if the instance is a Right', () => {
20 | expect(Right.of().isRight()).toBe(true);
21 | });
22 |
23 | test('It should return false if the instance is a Left', () => {
24 | expect(Left.of().isRight()).toBe(false);
25 | });
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/test/either/map.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 | const { Left, Right } = Either;
6 |
7 | describe('Data.Either', () => {
8 | describe('Either.map', () => {
9 | test('It should apply the transform if the instance is a Right', () => {
10 | const transform = x => x.toUpperCase();
11 | const either = Right.of('text');
12 |
13 | const result = Either.map(transform, either);
14 |
15 | expect(Either.isRight(result)).toBe(true);
16 | expect(result.value).toEqual('TEXT');
17 | });
18 |
19 | test('It should ignore the transform if the instance is a Left', () => {
20 | const transform = x => x.toUpperCase();
21 | const either = Left.of('text');
22 |
23 | const result = Either.map(transform, either);
24 |
25 | expect(Either.isLeft(result)).toBe(true);
26 | expect(result.value).toEqual('text');
27 | });
28 | });
29 |
30 | describe('Either#map', () => {
31 | test('It should apply the transform if the instance is a Right', () => {
32 | const result = Either
33 | .Right.of('text')
34 | .map(x => x.toUpperCase());
35 |
36 | expect(Either.isRight(result)).toBe(true);
37 | expect(result.value).toEqual('TEXT');
38 | });
39 |
40 | test('It should ignore the transform if the instance is a Left', () => {
41 | const result = Either
42 | .Left.of('text')
43 | .map(x => x.toUpperCase());
44 |
45 | expect(Either.isLeft(result)).toBe(true);
46 | expect(result.value).toEqual('text');
47 | });
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/test/either/of.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 | const { Left, Right } = Either;
6 |
7 | describe('Data.Either', () => {
8 | describe('Either.of', () => {
9 | test('It should return an instance of Either', () => {
10 | expect(Either.of(1) instanceof Either).toBe(true);
11 | });
12 | });
13 |
14 | describe('Either#of', () => {
15 | test('It should return an instance of Either', () => {
16 | expect(Either.of().of(1).isRight()).toBe(true);
17 | expect(Right.of().of(1).isRight()).toBe(true);
18 | expect(Left.of().of(1).isLeft()).toBe(true);
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/test/either/toMaybe.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 |
6 | describe('Data.Either', () => {
7 | describe('Either.toMaybe', () => {
8 | test('It should return a Just if the Either is Right', () => {
9 | const maybe = Either.toMaybe(Either.Right());
10 |
11 | expect(maybe.isJust()).toBe(true);
12 | });
13 |
14 | test('It should return a Nothing if the Either is Left', () => {
15 | const maybe = Either.toMaybe(Either.Left());
16 |
17 | expect(maybe.isNothing()).toBe(true);
18 | });
19 | });
20 |
21 | describe('Either#toMaybe', () => {
22 | test('It should return a Just if the instance is a Right', () => {
23 | expect(Either.Right.of().toMaybe().isJust()).toBe(true);
24 | });
25 |
26 | test('It should return a Nothing if the instance is a Left', () => {
27 | expect(Either.Left.of().toMaybe().isNothing()).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/either/toResult.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 |
6 | describe('Data.Either', () => {
7 | describe('Either.toResult', () => {
8 | test('It should return an Ok if the Either is Right', () => {
9 | const maybe = Either.toResult(Either.Right());
10 |
11 | expect(maybe.isOk()).toBe(true);
12 | });
13 |
14 | test('It should an Err if the Either is Left', () => {
15 | const maybe = Either.toResult(Either.Left());
16 |
17 | expect(maybe.isErr()).toBe(true);
18 | });
19 | });
20 |
21 | describe('Either#toResult', () => {
22 | test('It should return an Ok if the instance is a Right', () => {
23 | expect(Either.Right.of().toResult().isOk()).toBe(true);
24 | });
25 |
26 | test('It should return an Err if the instance is a Left', () => {
27 | expect(Either.Left.of().toResult().isErr()).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/either/toTask.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test, jest */
2 |
3 | const Either = require('../../src/either');
4 |
5 |
6 | describe('Data.Either', () => {
7 | describe('Either.toTask', () => {
8 | test('It should return a resolved task if the Either is Right', () => {
9 | const task = Either.toTask(Either.Right(1));
10 | const rejectedCallback = jest.fn();
11 | const resolvedCallback = jest.fn();
12 |
13 | task.fork(rejectedCallback, resolvedCallback);
14 |
15 | expect(rejectedCallback).not.toHaveBeenCalled();
16 | expect(resolvedCallback).toHaveBeenCalled();
17 | });
18 |
19 | test('It should return a rejected task if the Either is Left', () => {
20 | const task = Either.toTask(Either.Left(1));
21 | const rejectedCallback = jest.fn();
22 | const resolvedCallback = jest.fn();
23 |
24 | task.fork(rejectedCallback, resolvedCallback);
25 |
26 | expect(rejectedCallback).toHaveBeenCalled();
27 | expect(resolvedCallback).not.toHaveBeenCalled();
28 | });
29 | });
30 |
31 | describe('Either#toTask', () => {
32 | test('It should return a resolved task if the instance is a Right', () => {
33 | const task = Either.Right.of(1).toTask();
34 | const rejectedCallback = jest.fn();
35 | const resolvedCallback = jest.fn();
36 |
37 | task.fork(rejectedCallback, resolvedCallback);
38 |
39 | expect(rejectedCallback).not.toHaveBeenCalled();
40 | expect(resolvedCallback).toHaveBeenCalled();
41 | });
42 |
43 | test('It should return a rejected task if the instance is a Left', () => {
44 | const task = Either.Left.of(1).toTask();
45 | const rejectedCallback = jest.fn();
46 | const resolvedCallback = jest.fn();
47 |
48 | task.fork(rejectedCallback, resolvedCallback);
49 |
50 | expect(rejectedCallback).toHaveBeenCalled();
51 | expect(resolvedCallback).not.toHaveBeenCalled();
52 | });
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/test/either/toValidation.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 |
6 | describe('Data.Either', () => {
7 | describe('Either.toValidation', () => {
8 | test('It should return a Success if the Either is Right', () => {
9 | const validation = Either.toValidation(Either.Right());
10 |
11 | expect(validation.isSuccess()).toBe(true);
12 | });
13 |
14 | test('It should return a Failure if the Either is Left', () => {
15 | const validation = Either.toValidation(Either.Left());
16 |
17 | expect(validation.isFailure()).toBe(true);
18 | });
19 | });
20 |
21 | describe('Either#toValidation', () => {
22 | test('It should return a Success if the instance is a Right', () => {
23 | expect(Either.Right.of().toValidation().isSuccess()).toBe(true);
24 | });
25 |
26 | test('It should return a Failure if the instance is a Left', () => {
27 | expect(Either.Left.of().toValidation().isFailure()).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/either/try.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Either = require('../../src/either');
4 |
5 |
6 | describe('Data.Either', () => {
7 | describe('Either.try', () => {
8 | test('It should not throw an error.', () => {
9 | const wrapped = Either.try(() => {
10 | throw new Error('I threw.');
11 | });
12 |
13 | expect(() => wrapped).not.toThrow();
14 | });
15 |
16 | test('It returns the error that was thrown in a Left.', () => {
17 | const wrapped = Either.try(() => {
18 | throw new Error('I threw.');
19 | });
20 |
21 | const result = wrapped();
22 |
23 | expect(result.isLeft()).toBe(true);
24 | expect(result.value instanceof Error).toBe(true);
25 | expect(result.value.message).toBe('I threw.');
26 | });
27 |
28 | test('It returns the return value in a Right if nothing threw.', () => {
29 | const wrapped = Either.try(() => 'Hey, I am ok');
30 |
31 | const result = wrapped();
32 |
33 | expect(result.isRight()).toBe(true);
34 | expect(result.value).toBe('Hey, I am ok');
35 | });
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/test/io/andThen.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const IO = require('../../src/io');
4 |
5 | describe('IO', () => {
6 | describe('IO.andThen', () => {
7 | test('it should apply the transformation and flatten the IO that was returned', () => {
8 | const io = IO.of('foo');
9 | const toBaz = () => IO.of('baz');
10 |
11 | expect(IO.andThen(toBaz, io).run('bar')).toBe('baz');
12 | });
13 | });
14 |
15 | describe('IO#andThen', () => {
16 | test('it should apply the transformation and flatten the IO that was returned', () => {
17 | const io = IO.of('foo');
18 | const toBaz = () => IO.of('baz');
19 |
20 | expect(io.andThen(toBaz).run('bar')).toBe('baz');
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/io/ap.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const IO = require('../../src/io');
4 |
5 | describe('IO', () => {
6 | describe('IO.ap', () => {
7 | test('it should apply a regular function in an Apply and return a new IO with the result', () => {
8 | const io = IO.of('foo');
9 | const toBaz = IO.of(() => 'baz');
10 |
11 | expect(IO.ap(toBaz, io).run('bar')).toBe('baz');
12 | });
13 | });
14 |
15 | describe('IO#ap', () => {
16 | test('it should apply a regular function in an Apply and return a new IO with the result', () => {
17 | const io = IO.of('foo');
18 | const toBaz = IO.of(() => 'baz');
19 |
20 | expect(io.ap(toBaz).run('bar')).toBe('baz');
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/io/chain.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const IO = require('../../src/io');
4 |
5 | describe('IO', () => {
6 | describe('IO.chain', () => {
7 | test('it should apply the transformation and flatten the IO that was returned', () => {
8 | const io = IO.of('foo');
9 | const toBaz = () => IO.of('baz');
10 |
11 | expect(IO.chain(toBaz, io).run('bar')).toBe('baz');
12 | });
13 | });
14 |
15 | describe('IO#chain', () => {
16 | test('it should apply the transformation and flatten the IO that was returned', () => {
17 | const io = IO.of('foo');
18 | const toBaz = () => IO.of('baz');
19 |
20 | expect(io.chain(toBaz).run('bar')).toBe('baz');
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/io/map.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const IO = require('../../src/io');
4 |
5 | describe('IO', () => {
6 | describe('IO.map', () => {
7 | test('it should apply a regular function and return a new IO with the result', () => {
8 | const io = IO.of('foo');
9 | const toBaz = () => 'baz';
10 |
11 | expect(IO.map(toBaz, io).run('bar')).toBe('baz');
12 | });
13 | });
14 |
15 | describe('IO#map', () => {
16 | test('it should apply a regular function and return a new IO with the result', () => {
17 | const io = IO.of('foo');
18 | const toBaz = () => 'baz';
19 |
20 | expect(io.map(toBaz).run('bar')).toBe('baz');
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/io/of.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const IO = require('../../src/io');
4 |
5 | describe('IO', () => {
6 | describe('IO.of', () => {
7 | test('it should create a new io that will return the given value', () => {
8 | const io = IO.of('foo');
9 |
10 | expect(io.run()).toBe('foo');
11 | });
12 | });
13 |
14 | describe('IO#of', () => {
15 | test('it should create a new io that will return the given value', () => {
16 | const io = IO.of().of('foo');
17 |
18 | expect(io.run()).toBe('foo');
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/test/maybe/andThen.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 | describe('Data.Maybe', () => {
6 | describe('Maybe.andThen', () => {
7 | test('It should apply the transform if the instance is a Just', () => {
8 | const transform = x => x.toUpperCase();
9 | const maybe = Maybe.of('text');
10 |
11 | const result = Maybe.andThen(transform, maybe);
12 |
13 | expect(result).toBe('TEXT');
14 | });
15 |
16 | test('It should ignore the transform if the instance is a Nothing', () => {
17 | const transform = x => x.toUpperCase();
18 | const maybe = Maybe.Nothing;
19 |
20 | const result = Maybe.andThen(transform, maybe);
21 |
22 | expect(result).toBe(maybe);
23 | });
24 | });
25 |
26 | describe('Maybe#andThen', () => {
27 | test('It should apply the transform if the instance is a Just', () => {
28 | const result = Maybe.Just.of('text');
29 | const value = result.andThen(x => x.toUpperCase());
30 |
31 | expect(value).toBe('TEXT');
32 | });
33 |
34 | test('It should ignore the transform if the instance is a Nothing', () => {
35 | const result = Maybe.Nothing.of();
36 | const value = result.andThen(x => x.toUpperCase());
37 |
38 | expect(value).toBe(result);
39 | });
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/test/maybe/ap.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 | describe('Data.Maybe', () => {
6 | describe('Maybe.ap', () => {
7 | test('It should apply the transform if the instance is a Just', () => {
8 | const maybe = Maybe.of('text');
9 | const apply = Maybe.of(x => x.toUpperCase());
10 |
11 | const result = Maybe.ap(apply, maybe);
12 |
13 | expect(Maybe.isJust(result)).toBe(true);
14 | expect(result.value).toEqual('TEXT');
15 | });
16 |
17 | test('It should ignore the transform if the instance is a Nothing', () => {
18 | const maybe = Maybe.Nothing;
19 | const apply = Maybe.of(x => x.toUpperCase());
20 |
21 | const result = Maybe.ap(apply, maybe);
22 |
23 | expect(Maybe.isNothing(result)).toBe(true);
24 | });
25 | });
26 |
27 | describe('Maybe#ap', () => {
28 | test('It should apply the transform if the instance is a Just', () => {
29 | const result = Maybe
30 | .Just.of('text')
31 | .ap(Maybe.of(x => x.toUpperCase()));
32 |
33 | expect(Maybe.isJust(result)).toBe(true);
34 | expect(result.value).toBe('TEXT');
35 | });
36 |
37 | test('It should ignore the transform if the instance is a Nothing', () => {
38 | const result = Maybe
39 | .Nothing.of()
40 | .ap(Maybe.of(x => x.toUpperCase()));
41 |
42 | expect(Maybe.isNothing(result)).toBe(true);
43 | });
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/test/maybe/caseOf.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 | const { Nothing, Just } = Maybe;
6 |
7 |
8 | describe('Data.Maybe', () => {
9 | describe('Nothing#caseOf', () => {
10 | test('It should call the "Nothing" function.', () => {
11 | const nothingCB = jest.fn();
12 | const justCB = jest.fn();
13 |
14 | Nothing.caseOf({
15 | Nothing: nothingCB,
16 | Just: justCB,
17 | });
18 |
19 | expect(nothingCB.mock.calls.length).toBe(1);
20 | expect(justCB.mock.calls.length).toBe(0);
21 | });
22 | });
23 |
24 | describe('Just#caseOf', () => {
25 | test('It should call the "Just" function.', () => {
26 | const nothingCB = jest.fn();
27 | const justCB = jest.fn();
28 |
29 | Just().caseOf({
30 | Nothing: nothingCB,
31 | Just: justCB,
32 | });
33 |
34 | expect(nothingCB.mock.calls.length).toBe(0);
35 | expect(justCB.mock.calls.length).toBe(1);
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/maybe/cata.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 | const { Nothing, Just } = Maybe;
6 |
7 |
8 | describe('Data.Maybe', () => {
9 | describe('Nothing#cata', () => {
10 | test('It should call the "Nothing" function.', () => {
11 | const leftCB = jest.fn();
12 | const rightCB = jest.fn();
13 |
14 | Nothing.cata({
15 | Nothing: leftCB,
16 | Just: rightCB,
17 | });
18 |
19 | expect(leftCB.mock.calls.length).toBe(1);
20 | expect(rightCB.mock.calls.length).toBe(0);
21 | });
22 | });
23 |
24 | describe('Just#cata', () => {
25 | test('It should call the "Just" function.', () => {
26 | const leftCB = jest.fn();
27 | const rightCB = jest.fn();
28 |
29 | Just().cata({
30 | Nothing: leftCB,
31 | Just: rightCB,
32 | });
33 |
34 | expect(leftCB.mock.calls.length).toBe(0);
35 | expect(rightCB.mock.calls.length).toBe(1);
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/maybe/chain.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 | describe('Data.Maybe', () => {
6 | describe('Maybe.chain', () => {
7 | test('It should apply the transform if the instance is a Just', () => {
8 | const transform = x => x.toUpperCase();
9 | const maybe = Maybe.of('text');
10 |
11 | const result = Maybe.chain(transform, maybe);
12 |
13 | expect(result).toBe('TEXT');
14 | });
15 |
16 | test('It should ignore the transform if the instance is a Nothing', () => {
17 | const transform = x => x.toUpperCase();
18 | const maybe = Maybe.Nothing;
19 |
20 | const result = Maybe.chain(transform, maybe);
21 |
22 | expect(result).toBe(maybe);
23 | });
24 | });
25 |
26 | describe('Maybe#chain', () => {
27 | test('It should apply the transform if the instance is a Just', () => {
28 | const result = Maybe.Just.of('text');
29 | const value = result.chain(x => x.toUpperCase());
30 |
31 | expect(value).toBe('TEXT');
32 | });
33 |
34 | test('It should ignore the transform if the instance is a Nothing', () => {
35 | const result = Maybe.Nothing.of();
36 | const value = result.chain(x => x.toUpperCase());
37 |
38 | expect(value).toBe(result);
39 | });
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/test/maybe/fromNullable.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 | describe('Data.Maybe', () => {
6 | describe('Maybe.fromNullable', () => {
7 | test('It return a Nothing if the value is null or undefined', () => {
8 | expect(Maybe.fromNullable()).toBe(Maybe.Nothing);
9 | expect(Maybe.fromNullable(null)).toBe(Maybe.Nothing);
10 | expect(Maybe.fromNullable(undefined)).toBe(Maybe.Nothing);
11 | });
12 |
13 | test('It return a Just if the value is not null or undefined', () => {
14 | expect(Maybe.isJust(Maybe.fromNullable(''))).toBe(true);
15 | expect(Maybe.isJust(Maybe.fromNullable(true))).toBe(true);
16 | });
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/test/maybe/isJust.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 | describe('Data.Maybe', () => {
6 | describe('Maybe.isJust', () => {
7 | test('It should return true if the instance is a Just', () => {
8 | expect(Maybe.isJust(Maybe.Just.of())).toBe(true);
9 | });
10 |
11 | test('It should return false if the instance is a Nothing', () => {
12 | expect(Maybe.isJust(Maybe.Nothing)).toBe(false);
13 | });
14 | });
15 |
16 | describe('Maybe#isJust', () => {
17 | test('It should return true if the instance is a Just', () => {
18 | expect(Maybe.Just.of().isJust()).toBe(true);
19 | });
20 |
21 | test('It should return false if the instance is a Nothing', () => {
22 | expect(Maybe.Nothing.of().isJust()).toBe(false);
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/test/maybe/isNothing.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 | describe('Data.Maybe', () => {
6 | describe('Maybe.isNothing', () => {
7 | test('It should return true if the instance is a Nothing', () => {
8 | expect(Maybe.isNothing(Maybe.Nothing)).toBe(true);
9 | });
10 |
11 | test('It should return false if the instance is a Just', () => {
12 | expect(Maybe.isNothing(Maybe.Just.of())).toBe(false);
13 | });
14 | });
15 |
16 | describe('Maybe#isNothing', () => {
17 | test('It should return true if the instance is a Nothing', () => {
18 | expect(Maybe.Nothing.of().isNothing()).toBe(true);
19 | });
20 |
21 | test('It should return false if the instance is a Just', () => {
22 | expect(Maybe.Just.of().isNothing()).toBe(false);
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/test/maybe/map.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 | describe('Data.Maybe', () => {
6 | describe('Maybe.map', () => {
7 | test('It should apply the transform if the instance is a Just', () => {
8 | const transform = x => x.toUpperCase();
9 | const maybe = Maybe.of('text');
10 |
11 | const result = Maybe.map(transform, maybe);
12 |
13 | expect(Maybe.isJust(result)).toBe(true);
14 | expect(result.value).toEqual('TEXT');
15 | });
16 |
17 | test('It should ignore the transform if the instance is a Nothing', () => {
18 | const transform = x => x.toUpperCase();
19 | const maybe = Maybe.Nothing;
20 |
21 | const result = Maybe.map(transform, maybe);
22 |
23 | expect(Maybe.isNothing(result)).toBe(true);
24 | });
25 | });
26 |
27 | describe('Maybe#map', () => {
28 | test('It should apply the transform if the instance is a Just', () => {
29 | const result = Maybe
30 | .Just.of('text')
31 | .map(x => x.toUpperCase());
32 |
33 | expect(Maybe.isJust(result)).toBe(true);
34 | expect(result.value).toEqual('TEXT');
35 | });
36 |
37 | test('It should ignore the transform if the instance is a Nothing', () => {
38 | const result = Maybe
39 | .Nothing.of()
40 | .map(x => x.toUpperCase());
41 |
42 | expect(Maybe.isNothing(result)).toBe(true);
43 | });
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/test/maybe/of.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 | describe('Data.Maybe', () => {
6 | describe('Maybe.of', () => {
7 | test('It should return an instance of Maybe', () => {
8 | expect(Maybe.of(1) instanceof Maybe).toBe(true);
9 | });
10 | });
11 |
12 | describe('Maybe#of', () => {
13 | test('It should return an instance of Maybe', () => {
14 | const maybe = Maybe.of();
15 |
16 | expect(maybe.of(1).isJust()).toBe(true);
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/maybe/toEither.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 |
6 | describe('Data.Maybe', () => {
7 | describe('Maybe.toEither', () => {
8 | test('It should return a Right if the Maybe is Just', () => {
9 | const either = Maybe.toEither(Maybe.Just());
10 |
11 | expect(either.isRight()).toBe(true);
12 | });
13 |
14 | test('It should return a Left if the Maybe is Nothing', () => {
15 | const either = Maybe.toEither(Maybe.Nothing);
16 |
17 | expect(either.isLeft()).toBe(true);
18 | });
19 | });
20 |
21 | describe('Maybe#toEither', () => {
22 | test('It should return a Right if the instance is a Just', () => {
23 | expect(Maybe.Just.of().toEither().isRight()).toBe(true);
24 | });
25 |
26 | test('It should return a Left if the instance is a Nothing', () => {
27 | expect(Maybe.Nothing.of().toEither().isLeft()).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/maybe/toResult.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 |
6 | describe('Data.Maybe', () => {
7 | describe('Maybe.toResult', () => {
8 | test('It should return an Ok if the Maybe is Just', () => {
9 | const result = Maybe.toResult(Maybe.Just());
10 |
11 | expect(result.isOk()).toBe(true);
12 | });
13 |
14 | test('It should return an Err if the Maybe is Nothing', () => {
15 | const result = Maybe.toResult(Maybe.Nothing);
16 |
17 | expect(result.isErr()).toBe(true);
18 | });
19 | });
20 |
21 | describe('Maybe#toResult', () => {
22 | test('It should return an Ok if the instance is a Just', () => {
23 | expect(Maybe.Just.of().toResult().isOk()).toBe(true);
24 | });
25 |
26 | test('It should return an Err if the instance is a Nothing', () => {
27 | expect(Maybe.Nothing.of().toResult().isErr()).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/maybe/toTask.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test, jest */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 |
6 | describe('Data.Maybe', () => {
7 | describe('Maybe.toTask', () => {
8 | test('It should return a resolved task if the Maybe is Just', () => {
9 | const task = Maybe.toTask(Maybe.Just(1));
10 | const rejectedCallback = jest.fn();
11 | const resolvedCallback = jest.fn();
12 |
13 | task.fork(rejectedCallback, resolvedCallback);
14 |
15 | expect(rejectedCallback).not.toHaveBeenCalled();
16 | expect(resolvedCallback).toHaveBeenCalled();
17 | });
18 |
19 | test('It should return a rejected task if the Maybe is Nothing', () => {
20 | const task = Maybe.toTask(Maybe.Nothing);
21 | const rejectedCallback = jest.fn();
22 | const resolvedCallback = jest.fn();
23 |
24 | task.fork(rejectedCallback, resolvedCallback);
25 |
26 | expect(rejectedCallback).toHaveBeenCalled();
27 | expect(resolvedCallback).not.toHaveBeenCalled();
28 | });
29 | });
30 |
31 | describe('Maybe#toTask', () => {
32 | test('It should return a resolved task if the instance is a Just', () => {
33 | const task = Maybe.Just.of(1).toTask();
34 | const rejectedCallback = jest.fn();
35 | const resolvedCallback = jest.fn();
36 |
37 | task.fork(rejectedCallback, resolvedCallback);
38 |
39 | expect(rejectedCallback).not.toHaveBeenCalled();
40 | expect(resolvedCallback).toHaveBeenCalled();
41 | });
42 |
43 | test('It should return a rejected task if the instance is a Nothing', () => {
44 | const task = Maybe.Nothing.of().toTask();
45 | const rejectedCallback = jest.fn();
46 | const resolvedCallback = jest.fn();
47 |
48 | task.fork(rejectedCallback, resolvedCallback);
49 |
50 | expect(rejectedCallback).toHaveBeenCalled();
51 | expect(resolvedCallback).not.toHaveBeenCalled();
52 | });
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/test/maybe/toValidation.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 |
6 | describe('Data.Maybe', () => {
7 | describe('Maybe.toValidation', () => {
8 | test('It should return a Success if the Maybe is Just', () => {
9 | const validation = Maybe.toValidation(Maybe.Just());
10 |
11 | expect(validation.isSuccess()).toBe(true);
12 | });
13 |
14 | test('It should return a Failure if the Maybe is Nothing', () => {
15 | const validation = Maybe.toValidation(Maybe.Nothing);
16 |
17 | expect(validation.isFailure()).toBe(true);
18 | });
19 | });
20 |
21 | describe('Maybe#toValidation', () => {
22 | test('It should return a Success if the instance is a Just', () => {
23 | expect(Maybe.Just.of().toValidation().isSuccess()).toBe(true);
24 | });
25 |
26 | test('It should return a Failure if the instance is a Nothing', () => {
27 | expect(Maybe.Nothing.of().toValidation().isFailure()).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/maybe/withDefault.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Maybe = require('../../src/maybe');
4 |
5 | describe('Data.Maybe', () => {
6 | describe('Maybe.withDefault', () => {
7 | test('It should return the default if the instance is Nothing', () => {
8 | expect(Maybe.withDefault('foo', Maybe.Nothing)).toBe('foo');
9 | });
10 |
11 | test('It should return the value if the instance is a Just', () => {
12 | expect(Maybe.withDefault('foo', Maybe.Just('bar'))).toBe('bar');
13 | });
14 | });
15 |
16 | describe('Maybe#withDefault', () => {
17 | test('It should return the default if the instance is Nothing', () => {
18 | expect(Maybe.Nothing.withDefault('foo')).toBe('foo');
19 | });
20 |
21 | test('It should return the default if the instance is Nothing', () => {
22 | expect(Maybe.Just('bar').withDefault('foo')).toBe('bar');
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/test/reader/ap.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Reader = require('../../src/reader');
4 |
5 | describe('Reader', () => {
6 | describe('Reader.ap', () => {
7 | test('it should apply a regular function in an Apply and return a new Reader with the result', () => {
8 | const reader = Reader.of('foo');
9 | const toBaz = Reader.of(() => 'baz');
10 |
11 | expect(Reader.ap(toBaz, reader).run('bar')).toBe('baz');
12 | });
13 | });
14 |
15 | describe('Reader#ap', () => {
16 | test('it should apply a regular function in an Apply and return a new Reader with the result', () => {
17 | const reader = Reader.of('foo');
18 | const toBaz = Reader.of(() => 'baz');
19 |
20 | expect(reader.ap(toBaz).run('bar')).toBe('baz');
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/reader/chain.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Reader = require('../../src/reader');
4 |
5 | describe('Reader', () => {
6 | describe('Reader.chain', () => {
7 | test('it should apply the transformation and flatten the Reader that was returned', () => {
8 | const reader = Reader.of('foo');
9 | const toBaz = () => Reader.of('baz');
10 |
11 | expect(Reader.chain(toBaz, reader).run('bar')).toBe('baz');
12 | });
13 | });
14 |
15 | describe('Reader#chain', () => {
16 | test('it should apply the transformation and flatten the Reader that was returned', () => {
17 | const reader = Reader.of('foo');
18 | const toBaz = () => Reader.of('baz');
19 |
20 | expect(reader.chain(toBaz).run('bar')).toBe('baz');
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/reader/map.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Reader = require('../../src/reader');
4 |
5 | describe('Reader', () => {
6 | describe('Reader.map', () => {
7 | test('it should apply a regular function and return a new Reader with the result', () => {
8 | const reader = Reader.of('foo');
9 | const toBaz = () => 'baz';
10 |
11 | expect(Reader.map(toBaz, reader).run('bar')).toBe('baz');
12 | });
13 | });
14 |
15 | describe('Reader#map', () => {
16 | test('it should apply a regular function and return a new Reader with the result', () => {
17 | const reader = Reader.of('foo');
18 | const toBaz = () => 'baz';
19 |
20 | expect(reader.map(toBaz).run('bar')).toBe('baz');
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/reader/of.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Reader = require('../../src/reader');
4 |
5 | describe('Reader', () => {
6 | describe('Reader.of', () => {
7 | test('it should create a new reader that will return the given value', () => {
8 | const reader = Reader.of('foo');
9 |
10 | expect(reader.run()).toBe('foo');
11 | });
12 | });
13 |
14 | describe('Reader#of', () => {
15 | test('it should create a new reader that will return the given value', () => {
16 | const reader = Reader.of().of('foo');
17 |
18 | expect(reader.run()).toBe('foo');
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/test/readerT/andThen.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Reader = require('../../src/reader');
4 | const Maybe = require('../../src/maybe');
5 |
6 | const ReaderT = Reader.T(Maybe);
7 |
8 | describe('ReaderT', () => {
9 | describe('ReaderT.andThen', () => {
10 | test('it should apply the transformation and flatten the ReaderT that was returned', () => {
11 | const reader = ReaderT.of('foo');
12 | const toBaz = () => ReaderT.of('baz');
13 |
14 | expect(ReaderT.andThen(toBaz, reader).run('bar').value).toBe('baz');
15 | });
16 | });
17 |
18 | describe('ReaderT#andThen', () => {
19 | test('it should apply the transformation and flatten the ReaderT that was returned', () => {
20 | const reader = ReaderT.of('foo');
21 | const toBaz = () => ReaderT.of('baz');
22 |
23 | expect(reader.andThen(toBaz).run('bar').value).toBe('baz');
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/test/readerT/ap.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Reader = require('../../src/reader');
4 | const Maybe = require('../../src/maybe');
5 |
6 | const ReaderT = Reader.T(Maybe);
7 |
8 | describe('ReaderT', () => {
9 | describe('ReaderT.ap', () => {
10 | test('it should apply a regular function in an Apply and return a new ReaderT with the result', () => {
11 | const reader = ReaderT.of('foo');
12 | const toBaz = ReaderT.of(() => 'baz');
13 |
14 | expect(ReaderT.ap(toBaz, reader).run('bar').value).toBe('baz');
15 | });
16 | });
17 |
18 | describe('ReaderT#ap', () => {
19 | test('it should apply a regular function in an Apply and return a new ReaderT with the result', () => {
20 | const reader = ReaderT.of('foo');
21 | const toBaz = ReaderT.of(() => 'baz');
22 |
23 | expect(reader.ap(toBaz).run('bar').value).toBe('baz');
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/test/readerT/chain.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Reader = require('../../src/reader');
4 | const Maybe = require('../../src/maybe');
5 |
6 | const ReaderT = Reader.T(Maybe);
7 |
8 | describe('ReaderT', () => {
9 | describe('ReaderT.chain', () => {
10 | test('it should apply the transformation and flatten the ReaderT that was returned', () => {
11 | const reader = ReaderT.of('foo');
12 | const toBaz = () => ReaderT.of('baz');
13 |
14 | expect(ReaderT.chain(toBaz, reader).run('bar').value).toBe('baz');
15 | });
16 | });
17 |
18 | describe('ReaderT#chain', () => {
19 | test('it should apply the transformation and flatten the ReaderT that was returned', () => {
20 | const reader = ReaderT.of('foo');
21 | const toBaz = () => ReaderT.of('baz');
22 |
23 | expect(reader.chain(toBaz).run('bar').value).toBe('baz');
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/test/readerT/map.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Reader = require('../../src/reader');
4 | const Maybe = require('../../src/maybe');
5 |
6 | const ReaderT = Reader.T(Maybe);
7 |
8 | describe('ReaderT', () => {
9 | describe('ReaderT.map', () => {
10 | test('it should apply a regular function and return a new ReaderT with the result', () => {
11 | const reader = ReaderT.of('foo');
12 | const toBaz = () => 'baz';
13 |
14 | expect(ReaderT.map(toBaz, reader).run('bar').value).toBe('baz');
15 | });
16 | });
17 |
18 | describe('ReaderT#map', () => {
19 | test('it should apply a regular function and return a new ReaderT with the result', () => {
20 | const reader = ReaderT.of('foo');
21 | const toBaz = () => 'baz';
22 |
23 | expect(reader.map(toBaz).run('bar').value).toBe('baz');
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/test/readerT/of.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Reader = require('../../src/reader');
4 | const Maybe = require('../../src/maybe');
5 |
6 | const ReaderT = Reader.T(Maybe);
7 |
8 | describe('ReaderT', () => {
9 | describe('ReaderT.of', () => {
10 | test('it should create a new reader that will return the given value', () => {
11 | const reader = ReaderT.of('foo');
12 |
13 | expect(reader.run().value).toBe('foo');
14 | });
15 | });
16 |
17 | describe('ReaderT#of', () => {
18 | test('it should create a new reader that will return the given value', () => {
19 | const reader = ReaderT.of().of('foo');
20 |
21 | expect(reader.run().value).toBe('foo');
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/remote-data/andThen.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 | } = RemoteData;
11 |
12 |
13 | describe('Data.RemoteData', () => {
14 | describe('RemoteData.andThen', () => {
15 | test('It should call the callback for "Success" only', () => {
16 | const callback = jest.fn();
17 |
18 | // This should be called
19 | RemoteData.andThen(callback, Success());
20 | expect(callback.mock.calls.length).toBe(1);
21 |
22 | // This should not be called
23 | RemoteData.andThen(callback, NotAsked);
24 | RemoteData.andThen(callback, Loading);
25 | RemoteData.andThen(callback, Failure());
26 | expect(callback.mock.calls.length).toBe(1);
27 | });
28 | });
29 |
30 | describe('NotAsked.andThen', () => {
31 | test('It should return itself without calling the callback', () => {
32 | const callback = jest.fn(x => x);
33 |
34 | const result = RemoteData.andThen(callback, NotAsked);
35 |
36 | expect(result).toBe(NotAsked);
37 | expect(callback.mock.calls.length).toBe(0);
38 | });
39 | });
40 |
41 | describe('Loading.andThen', () => {
42 | test('It should return itself without calling the callback', () => {
43 | const callback = jest.fn(x => x);
44 |
45 | const result = RemoteData.andThen(callback, Loading);
46 |
47 | expect(result).toBe(Loading);
48 | expect(callback.mock.calls.length).toBe(0);
49 | });
50 | });
51 |
52 | describe('Failure#andThen', () => {
53 | test('It should return itself without calling the callback', () => {
54 | const callback = jest.fn(x => x);
55 |
56 | const result = RemoteData.Failure(1).andThen(callback);
57 |
58 | expect(result).toEqual(Failure(1));
59 | expect(callback.mock.calls.length).toBe(0);
60 | });
61 | });
62 |
63 | describe('Success#andThen', () => {
64 | test('It should call the callback and return the result', () => {
65 | const callback = jest.fn(x => x);
66 |
67 | const result = RemoteData.Success(1).andThen(callback);
68 |
69 | expect(result).toEqual(1);
70 | expect(callback.mock.calls.length).toBe(1);
71 | });
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/test/remote-data/ap.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 | } = RemoteData;
11 |
12 |
13 | describe('Data.RemoteData', () => {
14 | describe('RemoteData.ap', () => {
15 | test('It should call the callback for "Success" only', () => {
16 | const callback = jest.fn();
17 |
18 | // This should be called
19 | RemoteData.ap(Success(callback), Success());
20 | expect(callback.mock.calls.length).toBe(1);
21 |
22 | // This should not be called
23 | RemoteData.ap(Success(callback), NotAsked);
24 | RemoteData.ap(Success(callback), Loading);
25 | RemoteData.ap(Success(callback), Failure());
26 | expect(callback.mock.calls.length).toBe(1);
27 | });
28 | });
29 |
30 | describe('NotAsked.ap', () => {
31 | test('It should return itself without calling the callback', () => {
32 | const callback = jest.fn(x => x);
33 |
34 | const result = RemoteData.ap(Success(callback), NotAsked);
35 |
36 | expect(result).toBe(NotAsked);
37 | expect(callback.mock.calls.length).toBe(0);
38 | });
39 | });
40 |
41 | describe('Loading.ap', () => {
42 | test('It should return itself without calling the callback', () => {
43 | const callback = jest.fn(x => x);
44 |
45 | const result = RemoteData.ap(Success(callback), Loading);
46 |
47 | expect(result).toBe(Loading);
48 | expect(callback.mock.calls.length).toBe(0);
49 | });
50 | });
51 |
52 | describe('Failure#ap', () => {
53 | test('It should return itself without calling the callback', () => {
54 | const callback = jest.fn(x => x);
55 |
56 | const result = RemoteData.Failure(1).ap(Success(callback));
57 |
58 | expect(result).toEqual(Failure(1));
59 | expect(callback.mock.calls.length).toBe(0);
60 | });
61 | });
62 |
63 | describe('Success#ap', () => {
64 | test('It should call the callback and return the result', () => {
65 | const callback = jest.fn(x => x);
66 |
67 | const result = RemoteData.Success(1).ap(Success(callback));
68 |
69 | expect(result).toEqual(Success(1));
70 | expect(callback.mock.calls.length).toBe(1);
71 | });
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/test/remote-data/caseOf.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 | } = RemoteData;
11 |
12 |
13 | describe('Data.RemoteData', () => {
14 | describe('NotAsked.caseOf', () => {
15 | test('It should call the "NotAsked" function.', () => {
16 | const notAskedCB = jest.fn();
17 | const loadingCB = jest.fn();
18 | const failureCB = jest.fn();
19 | const successCB = jest.fn();
20 |
21 | NotAsked.caseOf({
22 | NotAsked: notAskedCB,
23 | Loading: loadingCB,
24 | Failure: failureCB,
25 | Success: successCB,
26 | });
27 |
28 | expect(notAskedCB.mock.calls.length).toBe(1);
29 | expect(loadingCB.mock.calls.length).toBe(0);
30 | expect(failureCB.mock.calls.length).toBe(0);
31 | expect(successCB.mock.calls.length).toBe(0);
32 | });
33 | });
34 |
35 | describe('Loading.caseOf', () => {
36 | test('It should call the "Loading" function.', () => {
37 | const notAskedCB = jest.fn();
38 | const loadingCB = jest.fn();
39 | const failureCB = jest.fn();
40 | const successCB = jest.fn();
41 |
42 | Loading.caseOf({
43 | NotAsked: notAskedCB,
44 | Loading: loadingCB,
45 | Failure: failureCB,
46 | Success: successCB,
47 | });
48 |
49 | expect(notAskedCB.mock.calls.length).toBe(0);
50 | expect(loadingCB.mock.calls.length).toBe(1);
51 | expect(failureCB.mock.calls.length).toBe(0);
52 | expect(successCB.mock.calls.length).toBe(0);
53 | });
54 | });
55 |
56 | describe('Failure#caseOf', () => {
57 | test('It should call the "Failure" function with the error.', () => {
58 | const notAskedCB = jest.fn();
59 | const loadingCB = jest.fn();
60 | const failureCB = jest.fn();
61 | const successCB = jest.fn();
62 | const value = 1;
63 |
64 | Failure(value).caseOf({
65 | NotAsked: notAskedCB,
66 | Loading: loadingCB,
67 | Failure: failureCB,
68 | Success: successCB,
69 | });
70 |
71 | expect(notAskedCB.mock.calls.length).toBe(0);
72 | expect(loadingCB.mock.calls.length).toBe(0);
73 | expect(failureCB.mock.calls.length).toBe(1);
74 | expect(successCB.mock.calls.length).toBe(0);
75 |
76 | expect(failureCB.mock.calls).toEqual([[value]]);
77 | });
78 | });
79 |
80 | describe('Success#caseOf', () => {
81 | test('It should call the "Success" function with the error.', () => {
82 | const notAskedCB = jest.fn();
83 | const loadingCB = jest.fn();
84 | const failureCB = jest.fn();
85 | const successCB = jest.fn();
86 | const value = 1;
87 |
88 | Success(value).caseOf({
89 | NotAsked: notAskedCB,
90 | Loading: loadingCB,
91 | Failure: failureCB,
92 | Success: successCB,
93 | });
94 |
95 | expect(notAskedCB.mock.calls.length).toBe(0);
96 | expect(loadingCB.mock.calls.length).toBe(0);
97 | expect(failureCB.mock.calls.length).toBe(0);
98 | expect(successCB.mock.calls.length).toBe(1);
99 |
100 | expect(successCB.mock.calls).toEqual([[value]]);
101 | });
102 | });
103 | });
104 |
--------------------------------------------------------------------------------
/test/remote-data/cata.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 | } = RemoteData;
11 |
12 |
13 | describe('Data.RemoteData', () => {
14 | describe('NotAsked.cata', () => {
15 | test('It should call the "NotAsked" function.', () => {
16 | const notAskedCB = jest.fn();
17 | const loadingCB = jest.fn();
18 | const failureCB = jest.fn();
19 | const successCB = jest.fn();
20 |
21 | NotAsked.cata({
22 | NotAsked: notAskedCB,
23 | Loading: loadingCB,
24 | Failure: failureCB,
25 | Success: successCB,
26 | });
27 |
28 | expect(notAskedCB.mock.calls.length).toBe(1);
29 | expect(loadingCB.mock.calls.length).toBe(0);
30 | expect(failureCB.mock.calls.length).toBe(0);
31 | expect(successCB.mock.calls.length).toBe(0);
32 | });
33 | });
34 |
35 | describe('Loading.cata', () => {
36 | test('It should call the "Loading" function.', () => {
37 | const notAskedCB = jest.fn();
38 | const loadingCB = jest.fn();
39 | const failureCB = jest.fn();
40 | const successCB = jest.fn();
41 |
42 | Loading.cata({
43 | NotAsked: notAskedCB,
44 | Loading: loadingCB,
45 | Failure: failureCB,
46 | Success: successCB,
47 | });
48 |
49 | expect(notAskedCB.mock.calls.length).toBe(0);
50 | expect(loadingCB.mock.calls.length).toBe(1);
51 | expect(failureCB.mock.calls.length).toBe(0);
52 | expect(successCB.mock.calls.length).toBe(0);
53 | });
54 | });
55 |
56 | describe('Failure#cata', () => {
57 | test('It should call the "Failure" function with the error.', () => {
58 | const notAskedCB = jest.fn();
59 | const loadingCB = jest.fn();
60 | const failureCB = jest.fn();
61 | const successCB = jest.fn();
62 | const value = 1;
63 |
64 | Failure(value).cata({
65 | NotAsked: notAskedCB,
66 | Loading: loadingCB,
67 | Failure: failureCB,
68 | Success: successCB,
69 | });
70 |
71 | expect(notAskedCB.mock.calls.length).toBe(0);
72 | expect(loadingCB.mock.calls.length).toBe(0);
73 | expect(failureCB.mock.calls.length).toBe(1);
74 | expect(successCB.mock.calls.length).toBe(0);
75 |
76 | expect(failureCB.mock.calls).toEqual([[value]]);
77 | });
78 | });
79 |
80 | describe('Success#cata', () => {
81 | test('It should call the "Success" function with the error.', () => {
82 | const notAskedCB = jest.fn();
83 | const loadingCB = jest.fn();
84 | const failureCB = jest.fn();
85 | const successCB = jest.fn();
86 | const value = 1;
87 |
88 | Success(value).cata({
89 | NotAsked: notAskedCB,
90 | Loading: loadingCB,
91 | Failure: failureCB,
92 | Success: successCB,
93 | });
94 |
95 | expect(notAskedCB.mock.calls.length).toBe(0);
96 | expect(loadingCB.mock.calls.length).toBe(0);
97 | expect(failureCB.mock.calls.length).toBe(0);
98 | expect(successCB.mock.calls.length).toBe(1);
99 |
100 | expect(successCB.mock.calls).toEqual([[value]]);
101 | });
102 | });
103 | });
104 |
--------------------------------------------------------------------------------
/test/remote-data/chain.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 | } = RemoteData;
11 |
12 |
13 | describe('Data.RemoteData', () => {
14 | describe('RemoteData.chain', () => {
15 | test('It should call the callback for "Success" only', () => {
16 | const callback = jest.fn();
17 |
18 | // This should be called
19 | RemoteData.chain(callback, Success());
20 | expect(callback.mock.calls.length).toBe(1);
21 |
22 | // This should not be called
23 | RemoteData.chain(callback, NotAsked);
24 | RemoteData.chain(callback, Loading);
25 | RemoteData.chain(callback, Failure());
26 | expect(callback.mock.calls.length).toBe(1);
27 | });
28 | });
29 |
30 | describe('NotAsked.chain', () => {
31 | test('It should return itself without calling the callback', () => {
32 | const callback = jest.fn(x => x);
33 |
34 | const result = RemoteData.chain(callback, NotAsked);
35 |
36 | expect(result).toBe(NotAsked);
37 | expect(callback.mock.calls.length).toBe(0);
38 | });
39 | });
40 |
41 | describe('Loading.chain', () => {
42 | test('It should return itself without calling the callback', () => {
43 | const callback = jest.fn(x => x);
44 |
45 | const result = RemoteData.chain(callback, Loading);
46 |
47 | expect(result).toBe(Loading);
48 | expect(callback.mock.calls.length).toBe(0);
49 | });
50 | });
51 |
52 | describe('Failure#chain', () => {
53 | test('It should return itself without calling the callback', () => {
54 | const callback = jest.fn(x => x);
55 |
56 | const result = RemoteData.Failure(1).chain(callback);
57 |
58 | expect(result).toEqual(Failure(1));
59 | expect(callback.mock.calls.length).toBe(0);
60 | });
61 | });
62 |
63 | describe('Success#chain', () => {
64 | test('It should call the callback and return the result', () => {
65 | const callback = jest.fn(x => x);
66 |
67 | const result = RemoteData.Success(1).chain(callback);
68 |
69 | expect(result).toEqual(1);
70 | expect(callback.mock.calls.length).toBe(1);
71 | });
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/test/remote-data/concat.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 |
11 | concat,
12 | } = RemoteData;
13 |
14 |
15 | describe('Data.RemoteData', () => {
16 | describe('RemoteData.concat', () => {
17 | test('It should place the first priority on "NotAsked"', () => {
18 | expect(concat(NotAsked, NotAsked)).toBe(NotAsked);
19 | expect(concat(NotAsked, Loading)).toBe(NotAsked);
20 | expect(concat(NotAsked, Failure())).toBe(NotAsked);
21 | expect(concat(NotAsked, Success())).toBe(NotAsked);
22 |
23 | expect(concat(NotAsked, NotAsked)).toBe(NotAsked);
24 | expect(concat(Loading, NotAsked)).toBe(NotAsked);
25 | expect(concat(Failure(), NotAsked)).toBe(NotAsked);
26 | expect(concat(Success(), NotAsked)).toBe(NotAsked);
27 | });
28 |
29 | test('It should place the second priority on "Loading"', () => {
30 | expect(concat(Loading, NotAsked)).toBe(NotAsked);
31 | expect(concat(Loading, Loading)).toBe(Loading);
32 | expect(concat(Loading, Failure())).toBe(Loading);
33 | expect(concat(Loading, Success())).toBe(Loading);
34 |
35 | expect(concat(NotAsked, Loading)).toBe(NotAsked);
36 | expect(concat(Loading, Loading)).toBe(Loading);
37 | expect(concat(Failure(), Loading)).toBe(Loading);
38 | expect(concat(Success(), Loading)).toBe(Loading);
39 | });
40 |
41 | test('It should place the third priority on "Failure" and only return the first one', () => {
42 | expect(concat(Failure(), NotAsked)).toBe(NotAsked);
43 | expect(concat(Failure(), Loading)).toBe(Loading);
44 | expect(concat(Failure(1), Failure(2))).toEqual(Failure(1));
45 | expect(concat(Failure(), Success())).toEqual(Failure());
46 |
47 | expect(concat(NotAsked, Failure())).toBe(NotAsked);
48 | expect(concat(Loading, Failure())).toBe(Loading);
49 | expect(concat(Failure(1), Failure(2))).toEqual(Failure(1));
50 | expect(concat(Success(), Failure())).toEqual(Failure());
51 | });
52 |
53 | test('If both instances are "Success"s, concat their values and put the result in a new "Success"', () => {
54 | expect(concat(Success(), NotAsked)).toBe(NotAsked);
55 | expect(concat(Success(), Loading)).toBe(Loading);
56 | expect(concat(Success(), Failure())).toEqual(Failure());
57 | expect(concat(Success('a'), Success('b'))).toEqual(Success('ab'));
58 |
59 | expect(concat(NotAsked, Success())).toBe(NotAsked);
60 | expect(concat(Loading, Success())).toBe(Loading);
61 | expect(concat(Failure(), Success())).toEqual(Failure());
62 | expect(concat(Success('a'), Success('b'))).toEqual(Success('ab'));
63 | });
64 | });
65 |
66 | describe('NotAsked.concat', () => {
67 | test('It should always return the "NotAsked" singleton', () => {
68 | expect(NotAsked.concat(NotAsked)).toBe(NotAsked);
69 | expect(NotAsked.concat(Loading)).toBe(NotAsked);
70 | expect(NotAsked.concat(Failure())).toBe(NotAsked);
71 | expect(NotAsked.concat(Success())).toBe(NotAsked);
72 | });
73 | });
74 |
75 | describe('Loading.concat', () => {
76 | test('It should always return the "Loading" singleton with priority to "NotAsked"', () => {
77 | expect(Loading.concat(NotAsked)).toBe(NotAsked);
78 | expect(Loading.concat(Loading)).toBe(Loading);
79 | expect(Loading.concat(Failure())).toBe(Loading);
80 | expect(Loading.concat(Success())).toBe(Loading);
81 | });
82 | });
83 |
84 | describe('Failure#concat', () => {
85 | test('It should return itself over "Success", with priority to "NotAsked" and Loading', () => {
86 | expect(Failure().concat(NotAsked)).toBe(NotAsked);
87 | expect(Failure().concat(Loading)).toBe(Loading);
88 | expect(Failure('a').concat(Failure('b'))).toEqual(Failure('a'));
89 | expect(Failure().concat(Success())).toEqual(Failure());
90 | });
91 | });
92 |
93 | describe('Success#concat', () => {
94 | test('It should return the other type unless it is also a "Success"', () => {
95 | expect(Success().concat(NotAsked)).toBe(NotAsked);
96 | expect(Success().concat(Loading)).toBe(Loading);
97 | expect(Success().concat(Failure())).toEqual(Failure());
98 | expect(Success('a').concat(Success('b'))).toEqual(Success('ab'));
99 | });
100 | });
101 | });
102 |
--------------------------------------------------------------------------------
/test/remote-data/isFailure.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 |
11 | isFailure,
12 | } = RemoteData;
13 |
14 |
15 | describe('Data.RemoteData', () => {
16 | describe('RemoteData.isFailure', () => {
17 | test('It should only return "true" for the "Failure" instances', () => {
18 | expect(isFailure(NotAsked)).toBe(false);
19 | expect(isFailure(Loading)).toBe(false);
20 | expect(isFailure(Failure())).toBe(true);
21 | expect(isFailure(Success())).toBe(false);
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/remote-data/isLoading.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 |
11 | isLoading,
12 | } = RemoteData;
13 |
14 |
15 | describe('Data.RemoteData', () => {
16 | describe('RemoteData.isLoading', () => {
17 | test('It should only return "true" for the "NotAsked" singleton', () => {
18 | expect(isLoading(NotAsked)).toBe(false);
19 | expect(isLoading(Loading)).toBe(true);
20 | expect(isLoading(Failure())).toBe(false);
21 | expect(isLoading(Success())).toBe(false);
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/remote-data/isNotAsked.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 |
11 | isNotAsked,
12 | } = RemoteData;
13 |
14 |
15 | describe('Data.RemoteData', () => {
16 | describe('RemoteData.isNotAsked', () => {
17 | test('It should only return "true" for the "NotAsked" singleton', () => {
18 | expect(isNotAsked(NotAsked)).toBe(true);
19 | expect(isNotAsked(Loading)).toBe(false);
20 | expect(isNotAsked(Failure())).toBe(false);
21 | expect(isNotAsked(Success())).toBe(false);
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/remote-data/isSuccess.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 |
11 | isSuccess,
12 | } = RemoteData;
13 |
14 |
15 | describe('Data.RemoteData', () => {
16 | describe('RemoteData.isSuccess', () => {
17 | test('It should only return "true" for the "Success" instances', () => {
18 | expect(isSuccess(NotAsked)).toBe(false);
19 | expect(isSuccess(Loading)).toBe(false);
20 | expect(isSuccess(Failure())).toBe(false);
21 | expect(isSuccess(Success())).toBe(true);
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/remote-data/map.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 | } = RemoteData;
11 |
12 |
13 | describe('Data.RemoteData', () => {
14 | describe('RemoteData.map', () => {
15 | test('It should call the callback for "Success" only', () => {
16 | const callback = jest.fn();
17 |
18 | // This should be called
19 | RemoteData.map(callback, Success());
20 | expect(callback.mock.calls.length).toBe(1);
21 |
22 | // This should not be called
23 | RemoteData.map(callback, NotAsked);
24 | RemoteData.map(callback, Loading);
25 | RemoteData.map(callback, Failure());
26 | expect(callback.mock.calls.length).toBe(1);
27 | });
28 | });
29 |
30 | describe('NotAsked.map', () => {
31 | test('It should return itself without calling the callback', () => {
32 | const callback = jest.fn(x => x);
33 |
34 | const result = RemoteData.map(callback, NotAsked);
35 |
36 | expect(result).toBe(NotAsked);
37 | expect(callback.mock.calls.length).toBe(0);
38 | });
39 | });
40 |
41 | describe('Loading.map', () => {
42 | test('It should return itself without calling the callback', () => {
43 | const callback = jest.fn(x => x);
44 |
45 | const result = RemoteData.map(callback, Loading);
46 |
47 | expect(result).toBe(Loading);
48 | expect(callback.mock.calls.length).toBe(0);
49 | });
50 | });
51 |
52 | describe('Failure#map', () => {
53 | test('It should return itself without calling the callback', () => {
54 | const callback = jest.fn(x => x);
55 |
56 | const result = RemoteData.Failure(1).map(callback);
57 |
58 | expect(result).toEqual(Failure(1));
59 | expect(callback.mock.calls.length).toBe(0);
60 | });
61 | });
62 |
63 | describe('Success#map', () => {
64 | test('It should call the callback and return the result', () => {
65 | const callback = jest.fn(x => x);
66 |
67 | const result = RemoteData.Success(1).map(callback);
68 |
69 | expect(result).toEqual(Success(1));
70 | expect(callback.mock.calls.length).toBe(1);
71 | });
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/test/remote-data/of.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 | } = RemoteData;
11 |
12 |
13 | describe('Data.RemoteData', () => {
14 | describe('RemoteData.of', () => {
15 | test('It should return an instance of Success', () => {
16 | expect(RemoteData.of(1)).toEqual(Success(1));
17 | });
18 | });
19 |
20 | describe('NotAsked.of', () => {
21 | test('It should return the "NotAsked" singleton', () => {
22 | expect(NotAsked.of()).toBe(NotAsked);
23 | });
24 | });
25 |
26 | describe('Loading.of', () => {
27 | test('It should return the "Loading" singleton', () => {
28 | expect(Loading.of()).toBe(Loading);
29 | });
30 | });
31 |
32 | describe('Success.of', () => {
33 | test('It should return an instance of Success', () => {
34 | expect(Success.of(1)).toEqual(Success(1));
35 | });
36 | });
37 |
38 | describe('Failure.of', () => {
39 | test('It should return an instance of Failure', () => {
40 | expect(Failure.of(1)).toEqual(Failure(1));
41 | });
42 | });
43 |
44 | describe('Failure#of', () => {
45 | test('It should return an instance of Failure', () => {
46 | expect(Failure().of(1)).toEqual(Failure(1));
47 | });
48 | });
49 |
50 | describe('Success#of', () => {
51 | test('It should return an instance of Success', () => {
52 | expect(Success().of(1)).toEqual(Success(1));
53 | });
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/test/remote-data/withDefault.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const RemoteData = require('../../src/remote-data');
4 |
5 | const {
6 | NotAsked,
7 | Loading,
8 | Failure,
9 | Success,
10 |
11 | withDefault,
12 | } = RemoteData;
13 |
14 |
15 | describe('Data.RemoteData', () => {
16 | describe('RemoteData.withDefault', () => {
17 | test('It should return the default unless the instance is a "Success"', () => {
18 | const DEFAULT = 'default';
19 | const FINAL = 'final';
20 |
21 | expect(withDefault(DEFAULT, NotAsked)).toBe(DEFAULT);
22 | expect(withDefault(DEFAULT, Loading)).toBe(DEFAULT);
23 | expect(withDefault(DEFAULT, Failure(FINAL))).toBe(DEFAULT);
24 | expect(withDefault(DEFAULT, Success(FINAL))).toBe(FINAL);
25 | });
26 | });
27 |
28 | describe('RemoteData.withDefault', () => {
29 | test('It should return the default unless the instance is a "Success"', () => {
30 | const DEFAULT = 'default';
31 | const FINAL = 'final';
32 |
33 | expect(NotAsked.withDefault(DEFAULT)).toBe(DEFAULT);
34 | expect(Loading.withDefault(DEFAULT)).toBe(DEFAULT);
35 | expect(Failure(FINAL).withDefault(DEFAULT)).toBe(DEFAULT);
36 | expect(Success(FINAL).withDefault(DEFAULT)).toBe(FINAL);
37 | });
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/test/result/andThen.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Result = require('../../src/result');
4 |
5 | describe('Data.Result', () => {
6 | describe('Result.andThen', () => {
7 | test('It should apply the transform if the instance is a Ok', () => {
8 | const transform = x => x.toUpperCase();
9 | const result = Result.of('text');
10 |
11 | const final = Result.andThen(transform, result);
12 |
13 | expect(final).toBe('TEXT');
14 | });
15 |
16 | test('It should ignore the transform if the instance is a Err', () => {
17 | const transform = x => x.toUpperCase();
18 | const result = Result.Err.of('text');
19 |
20 | const final = Result.andThen(transform, result);
21 |
22 | expect(final).toBe(result);
23 | });
24 | });
25 |
26 | describe('Result#andThen', () => {
27 | test('It should apply the transform if the instance is a Ok', () => {
28 | const final = Result.Ok.of('text');
29 | const value = final.andThen(x => x.toUpperCase());
30 |
31 | expect(value).toBe('TEXT');
32 | });
33 |
34 | test('It should ignore the transform if the instance is a Err', () => {
35 | const final = Result.Err.of('text');
36 | const value = final.andThen(x => x.toUpperCase());
37 |
38 | expect(value).toBe(final);
39 | });
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/test/result/ap.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Result = require('../../src/result');
4 |
5 | describe('Data.Result', () => {
6 | describe('Result.ap', () => {
7 | test('It should apply the transform if the instance is a Ok', () => {
8 | const result = Result.of('text');
9 | const apply = Result.of(x => x.toUpperCase());
10 |
11 | const final = Result.ap(apply, result);
12 |
13 | expect(Result.isOk(final)).toBe(true);
14 | expect(final.value).toEqual('TEXT');
15 | });
16 |
17 | test('It should ignore the transform if the instance is a Err', () => {
18 | const result = Result.Err.of('text');
19 | const apply = Result.of(x => x.toUpperCase());
20 |
21 | const final = Result.ap(apply, result);
22 |
23 | expect(Result.isErr(final)).toBe(true);
24 | expect(final.value).toEqual('text');
25 | });
26 | });
27 |
28 | describe('Result#ap', () => {
29 | test('It should apply the transform if the instance is a Ok', () => {
30 | const final = Result
31 | .Ok.of('text')
32 | .ap(Result.of(x => x.toUpperCase()));
33 |
34 | expect(Result.isOk(final)).toBe(true);
35 | expect(final.value).toBe('TEXT');
36 | });
37 |
38 | test('It should ignore the transform if the instance is a Err', () => {
39 | const final = Result
40 | .Err.of('text')
41 | .ap(Result.of(x => x.toUpperCase()));
42 |
43 | expect(Result.isErr(final)).toBe(true);
44 | expect(final.value).toBe('text');
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/test/result/caseOf.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const Result = require('../../src/result');
4 |
5 | const { Err, Ok } = Result;
6 |
7 |
8 | describe('Data.Result', () => {
9 | describe('Err#caseOf', () => {
10 | test('It should call the "Err" function.', () => {
11 | const errCB = jest.fn();
12 | const okCB = jest.fn();
13 |
14 | Err().caseOf({
15 | Err: errCB,
16 | Ok: okCB,
17 | });
18 |
19 | expect(errCB.mock.calls.length).toBe(1);
20 | expect(okCB.mock.calls.length).toBe(0);
21 | });
22 | });
23 |
24 | describe('Ok#caseOf', () => {
25 | test('It should call the "Ok" function.', () => {
26 | const errCB = jest.fn();
27 | const okCB = jest.fn();
28 |
29 | Ok().caseOf({
30 | Err: errCB,
31 | Ok: okCB,
32 | });
33 |
34 | expect(errCB.mock.calls.length).toBe(0);
35 | expect(okCB.mock.calls.length).toBe(1);
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/result/cata.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const Result = require('../../src/result');
4 |
5 | const { Err, Ok } = Result;
6 |
7 |
8 | describe('Data.Result', () => {
9 | describe('Err#cata', () => {
10 | test('It should call the "Err" function.', () => {
11 | const errCB = jest.fn();
12 | const okCB = jest.fn();
13 |
14 | Err().cata({
15 | Err: errCB,
16 | Ok: okCB,
17 | });
18 |
19 | expect(errCB.mock.calls.length).toBe(1);
20 | expect(okCB.mock.calls.length).toBe(0);
21 | });
22 | });
23 |
24 | describe('Ok#cata', () => {
25 | test('It should call the "Ok" function.', () => {
26 | const errCB = jest.fn();
27 | const okCB = jest.fn();
28 |
29 | Ok().cata({
30 | Err: errCB,
31 | Ok: okCB,
32 | });
33 |
34 | expect(errCB.mock.calls.length).toBe(0);
35 | expect(okCB.mock.calls.length).toBe(1);
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/result/chain.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Result = require('../../src/result');
4 |
5 | describe('Data.Result', () => {
6 | describe('Result.chain', () => {
7 | test('It should apply the transform if the instance is a Ok', () => {
8 | const transform = x => x.toUpperCase();
9 | const result = Result.of('text');
10 |
11 | const final = Result.chain(transform, result);
12 |
13 | expect(final).toBe('TEXT');
14 | });
15 |
16 | test('It should ignore the transform if the instance is a Err', () => {
17 | const transform = x => x.toUpperCase();
18 | const result = Result.Err.of('text');
19 |
20 | const final = Result.chain(transform, result);
21 |
22 | expect(final).toBe(result);
23 | });
24 | });
25 |
26 | describe('Result#chain', () => {
27 | test('It should apply the transform if the instance is a Ok', () => {
28 | const final = Result.Ok.of('text');
29 | const value = final.chain(x => x.toUpperCase());
30 |
31 | expect(value).toBe('TEXT');
32 | });
33 |
34 | test('It should ignore the transform if the instance is a Err', () => {
35 | const final = Result.Err.of('text');
36 | const value = final.chain(x => x.toUpperCase());
37 |
38 | expect(value).toBe(final);
39 | });
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/test/result/isErr.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Result = require('../../src/result');
4 |
5 | describe('Data.Result', () => {
6 | describe('Result.isErr', () => {
7 | test('It should return true if the instance is a Err', () => {
8 | expect(Result.isErr(Result.Err.of())).toBe(true);
9 | });
10 |
11 | test('It should return false if the instance is a Ok', () => {
12 | expect(Result.isErr(Result.Ok.of())).toBe(false);
13 | });
14 | });
15 |
16 | describe('Result#isErr', () => {
17 | test('It should return true if the instance is a Err', () => {
18 | expect(Result.Err.of().isErr()).toBe(true);
19 | });
20 |
21 | test('It should return false if the instance is a Ok', () => {
22 | expect(Result.Ok.of().isErr()).toBe(false);
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/test/result/isOk.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Result = require('../../src/result');
4 |
5 | describe('Data.Result', () => {
6 | describe('Result.isOk', () => {
7 | test('It should return true if the instance is a Ok', () => {
8 | expect(Result.isOk(Result.Ok.of())).toBe(true);
9 | });
10 |
11 | test('It should return false if the instance is a Err', () => {
12 | expect(Result.isOk(Result.Err.of())).toBe(false);
13 | });
14 | });
15 |
16 | describe('Result#isOk', () => {
17 | test('It should return true if the instance is a Ok', () => {
18 | expect(Result.Ok.of().isOk()).toBe(true);
19 | });
20 |
21 | test('It should return false if the instance is a Err', () => {
22 | expect(Result.Err.of().isOk()).toBe(false);
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/test/result/map.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Result = require('../../src/result');
4 |
5 | describe('Data.Result', () => {
6 | describe('Result.map', () => {
7 | test('It should apply the transform if the instance is a Ok', () => {
8 | const transform = x => x.toUpperCase();
9 | const result = Result.of('text');
10 |
11 | const final = Result.map(transform, result);
12 |
13 | expect(Result.isOk(final)).toBe(true);
14 | expect(final.value).toEqual('TEXT');
15 | });
16 |
17 | test('It should ignore the transform if the instance is a Err', () => {
18 | const transform = x => x.toUpperCase();
19 | const result = Result.Err.of('text');
20 |
21 | const final = Result.map(transform, result);
22 |
23 | expect(Result.isErr(final)).toBe(true);
24 | expect(final.value).toEqual('text');
25 | });
26 | });
27 |
28 | describe('Result#map', () => {
29 | test('It should apply the transform if the instance is a Ok', () => {
30 | const final = Result
31 | .Ok.of('text')
32 | .map(x => x.toUpperCase());
33 |
34 | expect(Result.isOk(final)).toBe(true);
35 | expect(final.value).toEqual('TEXT');
36 | });
37 |
38 | test('It should ignore the transform if the instance is a Err', () => {
39 | const final = Result
40 | .Err.of('text')
41 | .map(x => x.toUpperCase());
42 |
43 | expect(Result.isErr(final)).toBe(true);
44 | expect(final.value).toEqual('text');
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/test/result/of.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Result = require('../../src/result');
4 |
5 | describe('Data.Result', () => {
6 | describe('Result.of', () => {
7 | test('It should return an instance of Result', () => {
8 | expect(Result.of(1) instanceof Result).toBe(true);
9 | });
10 | });
11 |
12 | describe('Result#of', () => {
13 | test('It should return an instance of Result', () => {
14 | expect(Result.of().of(1).isOk()).toBe(true);
15 | expect(Result.Ok.of().of(1).isOk()).toBe(true);
16 | expect(Result.Err.of().of(1).isErr()).toBe(true);
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/result/toEither.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Result = require('../../src/result');
4 |
5 |
6 | describe('Data.Result', () => {
7 | describe('Result.toEither', () => {
8 | test('It should return a Right if the Result is Ok', () => {
9 | const either = Result.toEither(Result.Ok());
10 |
11 | expect(either.isRight()).toBe(true);
12 | });
13 |
14 | test('It should return a Left if the Result is an Err', () => {
15 | const either = Result.toEither(Result.Err());
16 |
17 | expect(either.isLeft()).toBe(true);
18 | });
19 | });
20 |
21 | describe('Result#toEither', () => {
22 | test('It should return a Right if the instance is a Ok', () => {
23 | expect(Result.Ok.of().toEither().isRight()).toBe(true);
24 | });
25 |
26 | test('It should return a Left if the instance is a Err', () => {
27 | expect(Result.Err.of().toEither().isLeft()).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/result/toMaybe.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Result = require('../../src/result');
4 |
5 |
6 | describe('Data.Result', () => {
7 | describe('Result.toMaybe', () => {
8 | test('It should return a Nothing if the Result is Ok', () => {
9 | const maybe = Result.toMaybe(Result.Ok());
10 |
11 | expect(maybe.isJust()).toBe(true);
12 | });
13 |
14 | test('It should return a Nothing if the Result is an Err', () => {
15 | const maybe = Result.toMaybe(Result.Err());
16 |
17 | expect(maybe.isNothing()).toBe(true);
18 | });
19 | });
20 |
21 | describe('Result#toMaybe', () => {
22 | test('It should return a Just if the instance is a Ok', () => {
23 | expect(Result.Ok.of().toMaybe().isJust()).toBe(true);
24 | });
25 |
26 | test('It should return a Nothing if the instance is a Err', () => {
27 | expect(Result.Err.of().toMaybe().isNothing()).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/result/toTask.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test, jest */
2 |
3 | const Result = require('../../src/result');
4 |
5 |
6 | describe('Data.Result', () => {
7 | describe('Result.toTask', () => {
8 | test('It should return a resolved task if the Result is Ok', () => {
9 | const task = Result.toTask(Result.Ok());
10 | const rejectedCallback = jest.fn();
11 | const resolvedCallback = jest.fn();
12 |
13 | task.fork(rejectedCallback, resolvedCallback);
14 |
15 | expect(rejectedCallback).not.toHaveBeenCalled();
16 | expect(resolvedCallback).toHaveBeenCalled();
17 | });
18 |
19 | test('It should return a rejected task if the Result is an Err', () => {
20 | const task = Result.toTask(Result.Err());
21 | const rejectedCallback = jest.fn();
22 | const resolvedCallback = jest.fn();
23 |
24 | task.fork(rejectedCallback, resolvedCallback);
25 |
26 | expect(rejectedCallback).toHaveBeenCalled();
27 | expect(resolvedCallback).not.toHaveBeenCalled();
28 | });
29 | });
30 |
31 | describe('Result#toTask', () => {
32 | test('It should return a resolved task if the instance is an Ok', () => {
33 | const task = Result.Ok.of().toTask();
34 | const rejectedCallback = jest.fn();
35 | const resolvedCallback = jest.fn();
36 |
37 | task.fork(rejectedCallback, resolvedCallback);
38 |
39 | expect(rejectedCallback).not.toHaveBeenCalled();
40 | expect(resolvedCallback).toHaveBeenCalled();
41 | });
42 |
43 | test('It should return a rejected task if the instance is an Err', () => {
44 | const task = Result.Err.of().toTask();
45 | const rejectedCallback = jest.fn();
46 | const resolvedCallback = jest.fn();
47 |
48 | task.fork(rejectedCallback, resolvedCallback);
49 |
50 | expect(rejectedCallback).toHaveBeenCalled();
51 | expect(resolvedCallback).not.toHaveBeenCalled();
52 | });
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/test/result/toValidation.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Result = require('../../src/result');
4 |
5 |
6 | describe('Data.Result', () => {
7 | describe('Result.toValidation', () => {
8 | test('It should return a Success if the Result is Ok', () => {
9 | const validation = Result.toValidation(Result.Ok());
10 |
11 | expect(validation.isSuccess()).toBe(true);
12 | });
13 |
14 | test('It should return a Failure if the Result is an Err', () => {
15 | const validation = Result.toValidation(Result.Err());
16 |
17 | expect(validation.isFailure()).toBe(true);
18 | });
19 | });
20 |
21 | describe('Result#toValidation', () => {
22 | test('It should return a Success if the instance is a Ok', () => {
23 | expect(Result.Ok.of().toValidation().isSuccess()).toBe(true);
24 | });
25 |
26 | test('It should return a Failure if the instance is a Err', () => {
27 | expect(Result.Err.of().toValidation().isFailure()).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/task/andThen.test.js:
--------------------------------------------------------------------------------
1 | /* eslint arrow-body-style: 0 */
2 | /* global describe, test, expect */
3 |
4 | const Task = require('../../src/task');
5 |
6 | describe('Data.Task', () => {
7 | describe('Task.andThen', () => {
8 | test('It should call the function and wait on the returned task.', () => {
9 | const task = Task.of('boom');
10 | const toUpper = Task.lift(x => x.toUpperCase());
11 |
12 | const result = Task.andThen(toUpper, task);
13 |
14 | return Task
15 | .toPromise(result)
16 | .then((value) => {
17 | expect(value).toBe('BOOM');
18 | });
19 | });
20 | });
21 |
22 | describe('Task#andThen', () => {
23 | test('It should call the function and wait on the returned task.', () => {
24 | return Task
25 | .of('boom')
26 | .andThen(Task.lift(x => x.toUpperCase()))
27 | .toPromise()
28 | .then((value) => {
29 | expect(value).toBe('BOOM');
30 | });
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/test/task/ap.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 |
3 | const Task = require('../../src/task');
4 |
5 | describe('Data.Task', () => {
6 | describe('Task.ap', () => {
7 | test('It should call the function with the inner value.', () => {
8 | const task = Task.of('boom');
9 | const toUpper = Task.of(x => x.toUpperCase());
10 |
11 | const result = Task.ap(toUpper, task);
12 |
13 | return Task
14 | .toPromise(result)
15 | .then((value) => {
16 | expect(value).toBe('BOOM');
17 | });
18 | });
19 | });
20 |
21 | describe('Task#ap', () => {
22 | test('It should call the function with the inner value.', () => {
23 | const toUpper = Task.of(x => x.toUpperCase());
24 |
25 | return Task
26 | .of('boom')
27 | .ap(toUpper)
28 | .toPromise()
29 | .then((value) => {
30 | expect(value).toBe('BOOM');
31 | });
32 | });
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/test/task/chain.test.js:
--------------------------------------------------------------------------------
1 | /* eslint arrow-body-style: 0 */
2 | /* global describe, test, expect */
3 |
4 | const Task = require('../../src/task');
5 |
6 | describe('Data.Task', () => {
7 | describe('Task.chain', () => {
8 | test('It should call the function and wait on the returned task.', () => {
9 | const task = Task.of('boom');
10 | const toUpper = Task.lift(x => x.toUpperCase());
11 |
12 | const result = Task.chain(toUpper, task);
13 |
14 | return Task
15 | .toPromise(result)
16 | .then((value) => {
17 | expect(value).toBe('BOOM');
18 | });
19 | });
20 | });
21 |
22 | describe('Task#chain', () => {
23 | test('It should call the function and wait on the returned task.', () => {
24 | return Task
25 | .of('boom')
26 | .chain(Task.lift(x => x.toUpperCase()))
27 | .toPromise()
28 | .then((value) => {
29 | expect(value).toBe('BOOM');
30 | });
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/test/task/fork.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 |
3 | const Task = require('../../src/task');
4 |
5 | describe('Data.Task', () => {
6 | describe('Task.fork', () => {
7 | test('Successful forks', () =>
8 | new Promise((resolve, reject) => {
9 | Task.fork(reject, resolve, Task.of('Wassup?'));
10 | })
11 | .then((result) => {
12 | expect(result).toBe('Wassup?');
13 | }));
14 |
15 | test('Unsuccessful forks', () =>
16 | new Promise((resolve, reject) => {
17 | Task.fork(reject, resolve, Task.reject('Wassup?'));
18 | })
19 | .catch((result) => {
20 | expect(result).toBe('Wassup?');
21 | }));
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/task/lift.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 |
3 | const Task = require('../../src/task');
4 |
5 | describe('Data.Task', () => {
6 | describe('Task.lift', () => {
7 | test('It should convert a regular function into a function that returns a task', () => {
8 | const add = (a, b) => a + b;
9 | const addTask = Task.lift(add);
10 |
11 | return Task
12 | .toPromise(addTask(1, 3))
13 | .then((value) => {
14 | expect(value).toBe(4);
15 | });
16 | });
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/test/task/liftNode.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 |
3 | const Task = require('../../src/task');
4 |
5 | describe('Data.Task', () => {
6 | describe('Task.liftNode', () => {
7 | test('It should convert a node style function into a function that returns a task', () => {
8 | const add = (a, b, cb) => cb(null, a + b);
9 | const addTask = Task.liftNode(add);
10 |
11 | return Task
12 | .toPromise(addTask(1, 4))
13 | .then((value) => {
14 | expect(value).toBe(5);
15 | });
16 | });
17 |
18 | test('It should reject with the error if there was one', () => {
19 | const add = (a, b, cb) => cb('error');
20 | const addTask = Task.liftNode(add);
21 |
22 | return Task
23 | .toPromise(addTask(1, 4))
24 | .catch((error) => {
25 | expect(error).toBe('error');
26 | });
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/test/task/liftPromise.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 |
3 | const Task = require('../../src/task');
4 |
5 | describe('Data.Task', () => {
6 | describe('Task.liftPromise', () => {
7 | test('It should convert a node style function into a function that returns a task', () => {
8 | const add = (a, b) =>
9 | Promise.resolve(a + b);
10 |
11 | const addTask = Task.liftPromise(add);
12 |
13 | return Task
14 | .toPromise(addTask(1, 4))
15 | .then((value) => {
16 | expect(value).toBe(5);
17 | });
18 | });
19 |
20 | test('It should reject with the error if there was one', () => {
21 | const add = () =>
22 | Promise.reject('error');
23 |
24 | const addTask = Task.liftPromise(add);
25 |
26 | return Task
27 | .toPromise(addTask(1, 4))
28 | .catch((error) => {
29 | expect(error).toBe('error');
30 | });
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/test/task/map.test.js:
--------------------------------------------------------------------------------
1 | /* eslint arrow-body-style: 0 */
2 | /* global describe, test, expect */
3 |
4 | const Task = require('../../src/task');
5 |
6 | describe('Data.Task', () => {
7 | describe('Task.map', () => {
8 | test('It should call the function with the inner value.', () => {
9 | const task = Task.of('boom');
10 | const toUpper = x => x.toUpperCase();
11 |
12 | const result = Task.map(toUpper, task);
13 |
14 | return Task
15 | .toPromise(result)
16 | .then((value) => {
17 | expect(value).toBe('BOOM');
18 | });
19 | });
20 | });
21 |
22 | describe('Task#map', () => {
23 | test('It should call the function with the inner value.', () => {
24 | return Task
25 | .of('boom')
26 | .map(x => x.toUpperCase())
27 | .toPromise()
28 | .then((value) => {
29 | expect(value).toBe('BOOM');
30 | });
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/test/task/of.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 |
3 | const Task = require('../../src/task');
4 |
5 | describe('Data.Task', () => {
6 | describe('Task.of', () => {
7 | test('It should create a new Task instance', () => {
8 | expect(Task.of() instanceof Task).toBe(true);
9 | });
10 | });
11 |
12 | describe('Task#of', () => {
13 | test('It should create a new Task instance', () => {
14 | expect(Task.of().of() instanceof Task).toBe(true);
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/test/task/parallel.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 |
3 | const Task = require('../../src/task');
4 |
5 | describe('Data.Task', () => {
6 | describe('Task.parallel', () => {
7 | test('It should resolve with an array of the resolved values', () => {
8 | const tasks = [
9 | Task.of('foo'),
10 | Task.of('bar'),
11 | Task.of('baz'),
12 | ];
13 |
14 | return Task
15 | .toPromise(Task.parallel(tasks))
16 | .then((value) => {
17 | expect(value).toEqual(['foo', 'bar', 'baz']);
18 | });
19 | });
20 |
21 | test('It should reject with the first error to occur', () => {
22 | const tasks = [
23 | Task.of('foo'),
24 | Task.of('bar'),
25 | Task.reject('baz'),
26 | ];
27 |
28 | return Task
29 | .toPromise(Task.parallel(tasks))
30 | .catch((value) => {
31 | expect(value).toEqual('baz');
32 | });
33 | });
34 |
35 | test('it should only call "reject" once', () => {
36 | let calls = 0;
37 | const rejectHandler = () => {
38 | calls += 1;
39 |
40 | if (calls > 1) {
41 | throw new Error('Called too many times.');
42 | }
43 | };
44 | const fail = () =>
45 | Task(reject => reject('bar'));
46 |
47 | const tasks = [
48 | Task.of('foo'),
49 | fail(),
50 | fail(),
51 | ];
52 |
53 | return Task
54 | .parallel(tasks)
55 | .fork(rejectHandler, () => {});
56 | });
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/test/task/recover.test.js:
--------------------------------------------------------------------------------
1 | /* eslint arrow-body-style: 0 */
2 | /* global describe, test, expect */
3 |
4 | const Task = require('../../src/task');
5 |
6 | describe('Data.Task', () => {
7 | describe('Task.recover', () => {
8 | test('It should allow you to convert a rejected Task into a resolved Task', () => {
9 | const task = Task.reject('some-value');
10 |
11 | return Task
12 | .toPromise(Task.recover(Task.of, task))
13 | .then((value) => {
14 | expect(value).toBe('some-value');
15 | });
16 | });
17 | });
18 |
19 | describe('Task#recover', () => {
20 | test('It should allow you to convert a rejected Task into a resolved Task', () => {
21 | return Task
22 | .reject('some-value')
23 | .recover(Task.of)
24 | .toPromise()
25 | .then((value) => {
26 | expect(value).toBe('some-value');
27 | });
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/task/reject.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 |
3 | const Task = require('../../src/task');
4 |
5 | describe('Data.Task', () => {
6 | describe('Task.reject', () => {
7 | test('It should create a new Task instance', () => {
8 | expect(Task.reject() instanceof Task).toBe(true);
9 | });
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/test/task/toPromise.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 |
3 | const Task = require('../../src/task');
4 |
5 | describe('Data.Task', () => {
6 | describe('Task.toPromise', () => {
7 | test('It should return a valid Promise', () => {
8 | expect(Task.toPromise(Task.of()) instanceof Promise).toBe(true);
9 | });
10 | });
11 |
12 | describe('Task#toPromise', () => {
13 | test('It should return a valid Promise', () => {
14 | expect(Task.of().toPromise() instanceof Promise).toBe(true);
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/test/tuple/equals.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Tuple = require('../../src/tuple');
4 |
5 | describe('Data.Tuple', () => {
6 | describe('Tuple.equals', () => {
7 | test('It should return true if both tuples contain arguments that pass ===', () => {
8 | const tupleA = Tuple(1, 2);
9 | const tupleB = Tuple(1, 2);
10 |
11 | expect(Tuple.equals(tupleA, tupleB)).toBe(true);
12 | expect(Tuple.equals(tupleB, tupleA)).toBe(true);
13 | });
14 |
15 | test('It should return false if any elements cant pass ===', () => {
16 | const tupleA = Tuple(1, 2);
17 | const tupleB = Tuple(3, 4);
18 |
19 | expect(Tuple.equals(tupleA, tupleB)).toBe(false);
20 | expect(Tuple.equals(tupleB, tupleA)).toBe(false);
21 | });
22 | });
23 |
24 | describe('Tuple#equals', () => {
25 | test('It should return true if both tuples contain arguments that pass ===', () => {
26 | const tupleA = Tuple(1, 2);
27 | const tupleB = Tuple(1, 2);
28 |
29 | expect(tupleA.equals(tupleB)).toBe(true);
30 | expect(tupleB.equals(tupleA)).toBe(true);
31 | });
32 |
33 | test('It should return false if any elements dont pass ===', () => {
34 | const tupleA = Tuple(1, 2);
35 | const tupleB = Tuple(3, 4);
36 |
37 | expect(tupleA.equals(tupleB)).toBe(false);
38 | expect(tupleB.equals(tupleA)).toBe(false);
39 | });
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/test/tuple/fst.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Tuple = require('../../src/tuple');
4 |
5 | describe('Data.Tuple', () => {
6 | describe('Tuple.fst', () => {
7 | test('It should return the first element of the tuple', () => {
8 | const tuple = Tuple(1, 2);
9 |
10 | expect(Tuple.fst(tuple)).toBe(1);
11 | });
12 | });
13 |
14 | describe('Tuple#fst', () => {
15 | test('It should return the first element of the tuple', () => {
16 | const tuple = Tuple(1, 2);
17 |
18 | expect(tuple.fst()).toBe(1);
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/test/tuple/map.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Tuple = require('../../src/tuple');
4 |
5 | describe('Data.Tuple', () => {
6 | describe('Tuple.map', () => {
7 | test('It should apply the function to the second element of the tuple', () => {
8 | const tuple = Tuple(1, 2);
9 | const inc = n => n + 1;
10 |
11 | expect(Tuple.snd(Tuple.map(inc, tuple))).toBe(3);
12 | });
13 | });
14 |
15 | describe('Tuple#map', () => {
16 | test('It should apply the function to the second element of the tuple', () => {
17 | const tuple = Tuple(1, 2);
18 | const inc = n => n + 1;
19 |
20 | expect(tuple.map(inc).snd()).toBe(3);
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/tuple/mapLeft.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Tuple = require('../../src/tuple');
4 |
5 | describe('Data.Tuple', () => {
6 | describe('Tuple.mapLeft', () => {
7 | test('It should apply the function to the second element of the tuple', () => {
8 | const tuple = Tuple(1, 2);
9 | const inc = n => n + 1;
10 |
11 | expect(Tuple.fst(Tuple.mapLeft(inc, tuple))).toBe(2);
12 | });
13 | });
14 |
15 | describe('Tuple#mapLeft', () => {
16 | test('It should apply the function to the second element of the tuple', () => {
17 | const tuple = Tuple(1, 2);
18 | const inc = n => n + 1;
19 |
20 | expect(tuple.mapLeft(inc).fst()).toBe(2);
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/tuple/snd.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 | const Tuple = require('../../src/tuple');
3 |
4 | describe('Data.Tuple', () => {
5 | describe('Tuple.snd', () => {
6 | test('It should return the second element of the tuple', () => {
7 | const tuple = Tuple(1, 2);
8 |
9 | expect(Tuple.snd(tuple)).toBe(2);
10 | });
11 | });
12 |
13 | describe('Tuple#snd', () => {
14 | test('It should return the second element of the tuple', () => {
15 | const tuple = Tuple(1, 2);
16 |
17 | expect(tuple.snd()).toBe(2);
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/test/tuple/toString.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Tuple = require('../../src/tuple');
4 |
5 | describe('Data.Tuple', () => {
6 | test('Tuple#toString()', () => {
7 | const tuple = Tuple(1, 2);
8 |
9 | expect(tuple.toString()).toBe('(1, 2)');
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/test/validation/andThen.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 | describe('Data.Validation', () => {
6 | describe('Validation.andThen', () => {
7 | test('It should apply the transform if the instance is a Success', () => {
8 | const transform = x => x.toUpperCase();
9 | const either = Validation.of('text');
10 |
11 | const result = Validation.andThen(transform, either);
12 |
13 | expect(result).toBe('TEXT');
14 | });
15 |
16 | test('It should ignore the transform if the instance is a Failure', () => {
17 | const transform = x => x.toUpperCase();
18 | const either = Validation.Failure.of('text');
19 |
20 | const result = Validation.andThen(transform, either);
21 |
22 | expect(result).toBe(either);
23 | });
24 | });
25 |
26 | describe('Validation#andThen', () => {
27 | test('It should apply the transform if the instance is a Success', () => {
28 | const transform = x => x.toUpperCase();
29 |
30 | const result = Validation.of('text').andThen(transform);
31 |
32 | expect(result).toBe('TEXT');
33 | });
34 |
35 | test('It should ignore the transform if the instance is a Failure', () => {
36 | const transform = x => x.toUpperCase();
37 | const either = Validation.Failure.of('text');
38 | const result = either.andThen(transform);
39 |
40 | expect(result).toBe(either);
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/validation/ap.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 | describe('Data.Validation', () => {
6 | describe('Validation.ap', () => {
7 | test('It should apply the transform if the instance is a Success', () => {
8 | const either = Validation.of('text');
9 | const apply = Validation.of(x => x.toUpperCase());
10 |
11 | const result = Validation.ap(apply, either);
12 |
13 | expect(Validation.isSuccess(result)).toBe(true);
14 | expect(result.value).toEqual('TEXT');
15 | });
16 |
17 | test('It should ignore the transform if the instance is a Failure', () => {
18 | const either = Validation.Failure.of('text');
19 | const apply = Validation.of(x => x.toUpperCase());
20 |
21 | const result = Validation.ap(apply, either);
22 |
23 | expect(Validation.isFailure(result)).toBe(true);
24 | expect(result.value).toEqual('text');
25 | });
26 | });
27 |
28 | describe('Validation#ap', () => {
29 | test('It should apply the transform if the instance is a Success', () => {
30 | const result = Validation
31 | .of('text')
32 | .ap(Validation.of(x => x.toUpperCase()));
33 |
34 | expect(result.isSuccess()).toBe(true);
35 | expect(result.value).toEqual('TEXT');
36 | });
37 |
38 | test('It should ignore the transform if the instance is a Failure', () => {
39 | const result = Validation
40 | .Failure.of('text')
41 | .ap(Validation.of(x => x.toUpperCase()));
42 |
43 | expect(result.isSuccess()).toBe(false);
44 | expect(result.value).toEqual('text');
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/test/validation/caseOf.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 | const { Failure, Success } = Validation;
6 |
7 |
8 | describe('Data.Validation', () => {
9 | describe('Failure#caseOf', () => {
10 | test('It should call the "Failure" function.', () => {
11 | const failureCB = jest.fn();
12 | const successCB = jest.fn();
13 |
14 | Failure().caseOf({
15 | Failure: failureCB,
16 | Success: successCB,
17 | });
18 |
19 | expect(failureCB.mock.calls.length).toBe(1);
20 | expect(successCB.mock.calls.length).toBe(0);
21 | });
22 | });
23 |
24 | describe('Success#caseOf', () => {
25 | test('It should call the "Success" function.', () => {
26 | const failureCB = jest.fn();
27 | const successCB = jest.fn();
28 |
29 | Success().caseOf({
30 | Failure: failureCB,
31 | Success: successCB,
32 | });
33 |
34 | expect(failureCB.mock.calls.length).toBe(0);
35 | expect(successCB.mock.calls.length).toBe(1);
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/validation/cata.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, jest, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 | const { Failure, Success } = Validation;
6 |
7 |
8 | describe('Data.Validation', () => {
9 | describe('Failure#cata', () => {
10 | test('It should call the "Failure" function.', () => {
11 | const failureCB = jest.fn();
12 | const successCB = jest.fn();
13 |
14 | Failure().cata({
15 | Failure: failureCB,
16 | Success: successCB,
17 | });
18 |
19 | expect(failureCB.mock.calls.length).toBe(1);
20 | expect(successCB.mock.calls.length).toBe(0);
21 | });
22 | });
23 |
24 | describe('Success#cata', () => {
25 | test('It should call the "Success" function.', () => {
26 | const failureCB = jest.fn();
27 | const successCB = jest.fn();
28 |
29 | Success().cata({
30 | Failure: failureCB,
31 | Success: successCB,
32 | });
33 |
34 | expect(failureCB.mock.calls.length).toBe(0);
35 | expect(successCB.mock.calls.length).toBe(1);
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/validation/chain.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 | describe('Data.Validation', () => {
6 | describe('Validation.chain', () => {
7 | test('It should apply the transform if the instance is a Success', () => {
8 | const transform = x => x.toUpperCase();
9 | const either = Validation.of('text');
10 |
11 | const result = Validation.chain(transform, either);
12 |
13 | expect(result).toBe('TEXT');
14 | });
15 |
16 | test('It should ignore the transform if the instance is a Failure', () => {
17 | const transform = x => x.toUpperCase();
18 | const either = Validation.Failure.of('text');
19 |
20 | const result = Validation.chain(transform, either);
21 |
22 | expect(result).toBe(either);
23 | });
24 | });
25 |
26 | describe('Validation#chain', () => {
27 | test('It should apply the transform if the instance is a Success', () => {
28 | const transform = x => x.toUpperCase();
29 |
30 | const result = Validation.of('text').chain(transform);
31 |
32 | expect(result).toBe('TEXT');
33 | });
34 |
35 | test('It should ignore the transform if the instance is a Failure', () => {
36 | const transform = x => x.toUpperCase();
37 | const either = Validation.Failure.of('text');
38 | const result = either.chain(transform);
39 |
40 | expect(result).toBe(either);
41 | });
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/validation/combine.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 | describe('Data.Validation', () => {
6 | describe('Validation.combine', () => {
7 | test('It should reduce all validations down a single validation.', () => {
8 | const failedA = Validation.combine([
9 | Validation.Failure(),
10 | Validation.Success(),
11 | Validation.Success(),
12 | ]);
13 |
14 | const failedB = Validation.combine([
15 | Validation.Success(),
16 | Validation.Failure(),
17 | Validation.Success(),
18 | ]);
19 |
20 | const failedC = Validation.combine([
21 | Validation.Success(),
22 | Validation.Success(),
23 | Validation.Failure(),
24 | ]);
25 |
26 | const success = Validation.combine([
27 | Validation.Success(),
28 | Validation.Success(),
29 | Validation.Success(),
30 | ]);
31 |
32 | expect(success.isSuccess()).toBe(true);
33 | expect(failedA.isFailure()).toBe(true);
34 | expect(failedB.isFailure()).toBe(true);
35 | expect(failedC.isFailure()).toBe(true);
36 | });
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/test/validation/concat.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 | describe('Data.Validation', () => {
6 | describe('Validation.concat', () => {
7 | test('It should return the failure if they are mixed', () => {
8 | const left = Validation.Failure();
9 | const right = Validation.Success();
10 |
11 | expect(Validation.concat(left, right)).toBe(left);
12 | expect(Validation.concat(right, left)).toBe(left);
13 | });
14 |
15 | test('It should concat the successes if they are both successes', () => {
16 | const left = Validation.Success(['a']);
17 | const right = Validation.Success(['b']);
18 |
19 | expect(Validation.concat(left, right).value).toEqual(['a', 'b']);
20 | expect(Validation.concat(right, left).value).toEqual(['b', 'a']);
21 | });
22 |
23 | test('It should concat the failures if they are both failures', () => {
24 | const left = Validation.Failure(['a']);
25 | const right = Validation.Failure(['b']);
26 |
27 | expect(Validation.concat(left, right).value).toEqual(['a', 'b']);
28 | expect(Validation.concat(right, left).value).toEqual(['b', 'a']);
29 | });
30 | });
31 |
32 | describe('Validation#concat', () => {
33 | test('It should return the failure if they are mixed', () => {
34 | const left = Validation.Failure();
35 | const right = Validation.Success();
36 |
37 | expect(right.concat(left)).toBe(left);
38 | expect(left.concat(right)).toBe(left);
39 | });
40 |
41 | test('It should concat the successes if they are both successes', () => {
42 | const left = Validation.Success(['a']);
43 | const right = Validation.Success(['b']);
44 |
45 | expect(right.concat(left).value).toEqual(['a', 'b']);
46 | expect(left.concat(right).value).toEqual(['b', 'a']);
47 | });
48 |
49 | test('It should concat the failures if they are both failures', () => {
50 | const left = Validation.Failure(['a']);
51 | const right = Validation.Failure(['b']);
52 |
53 | expect(right.concat(left).value).toEqual(['a', 'b']);
54 | expect(left.concat(right).value).toEqual(['b', 'a']);
55 | });
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/test/validation/empty.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 | describe('Data.Validation', () => {
6 | describe('Validation.empty', () => {
7 | test('It should return a Success of an array', () => {
8 | expect(Validation.empty()).toEqual(Validation.Success([]));
9 | });
10 | });
11 |
12 | describe('Validation#empty', () => {
13 | test('It should return a Success of an array', () => {
14 | expect(Validation.of().empty()).toEqual(Validation.Success([]));
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/test/validation/isFailure.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 | describe('Data.Validation', () => {
6 | describe('Validation.isFailure', () => {
7 | test('It should return true if the instance is a Failure', () => {
8 | expect(Validation.isFailure(Validation.Failure.of())).toBe(true);
9 | });
10 |
11 | test('It should return false if the instance is a Success', () => {
12 | expect(Validation.isFailure(Validation.Success.of())).toBe(false);
13 | });
14 | });
15 |
16 | describe('Validation#isFailure', () => {
17 | test('It should return true if the instance is a Failure', () => {
18 | expect(Validation.Failure.of().isFailure()).toBe(true);
19 | });
20 |
21 | test('It should return false if the instance is a Success', () => {
22 | expect(Validation.Success.of().isFailure()).toBe(false);
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/test/validation/isSuccess.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 | describe('Data.Validation', () => {
6 | describe('Validation.isSuccess', () => {
7 | test('It should return true if the instance is a Success', () => {
8 | expect(Validation.isSuccess(Validation.Success.of())).toBe(true);
9 | });
10 |
11 | test('It should return false if the instance is a Failure', () => {
12 | expect(Validation.isSuccess(Validation.Failure.of())).toBe(false);
13 | });
14 | });
15 |
16 | describe('Validation#isSuccess', () => {
17 | test('It should return true if the instance is a Success', () => {
18 | expect(Validation.Success.of().isSuccess()).toBe(true);
19 | });
20 |
21 | test('It should return false if the instance is a Failure', () => {
22 | expect(Validation.Failure.of().isSuccess()).toBe(false);
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/test/validation/map.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 | describe('Data.Validation', () => {
6 | describe('Validation.map', () => {
7 | test('It should apply the transform if the instance is a Success', () => {
8 | const transform = x => x.toUpperCase();
9 | const either = Validation.of('text');
10 |
11 | const result = Validation.map(transform, either);
12 |
13 | expect(Validation.isSuccess(result)).toBe(true);
14 | expect(result.value).toEqual('TEXT');
15 | });
16 |
17 | test('It should ignore the transform if the instance is a Failure', () => {
18 | const transform = x => x.toUpperCase();
19 | const either = Validation.Failure.of('text');
20 |
21 | const result = Validation.map(transform, either);
22 |
23 | expect(Validation.isFailure(result)).toBe(true);
24 | expect(result.value).toEqual('text');
25 | });
26 | });
27 |
28 | describe('Validation#map', () => {
29 | test('It should apply the transform if the instance is a Success', () => {
30 | const transform = x => x.toUpperCase();
31 |
32 | const result = Validation.of('text').map(transform);
33 |
34 | expect(result.isSuccess()).toBe(true);
35 | expect(result.value).toEqual('TEXT');
36 | });
37 |
38 | test('It should ignore the transform if the instance is a Failure', () => {
39 | const transform = x => x.toUpperCase();
40 |
41 | const result = Validation.Failure.of('text').map(transform);
42 |
43 | expect(result.isFailure()).toBe(true);
44 | expect(result.value).toEqual('text');
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/test/validation/of.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 | describe('Data.Validation', () => {
6 | describe('Validation.of', () => {
7 | test('It should return an instance of Validation', () => {
8 | expect(Validation.of(1).isSuccess()).toBe(true);
9 | });
10 | });
11 |
12 | describe('Validation#of', () => {
13 | test('It should return an instance of Validation', () => {
14 | expect(Validation.of().of().isSuccess()).toBe(true);
15 | expect(Validation.Success.of().of().isSuccess()).toBe(true);
16 | expect(Validation.Failure.of().of().isFailure()).toBe(true);
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/test/validation/toEither.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 |
6 | describe('Data.Validation', () => {
7 | describe('Validation.toEither', () => {
8 | test('It should return a Right if the Validation is a Success', () => {
9 | const either = Validation.toEither(Validation.Success());
10 |
11 | expect(either.isRight()).toBe(true);
12 | });
13 |
14 | test('It should return a Left if the Validation is a Failure', () => {
15 | const either = Validation.toEither(Validation.Failure());
16 |
17 | expect(either.isLeft()).toBe(true);
18 | });
19 | });
20 |
21 | describe('Validation#toEither', () => {
22 | test('It should return a Right if the instance is a Success', () => {
23 | expect(Validation.Success.of().toEither().isRight()).toBe(true);
24 | });
25 |
26 | test('It should return a Left if the instance is a Failure', () => {
27 | expect(Validation.Failure.of().toEither().isLeft()).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/validation/toMaybe.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 |
6 | describe('Data.Validation', () => {
7 | describe('Validation.toMaybe', () => {
8 | test('It should return a Nothing if the Validation is a Success', () => {
9 | const maybe = Validation.toMaybe(Validation.Success());
10 |
11 | expect(maybe.isJust()).toBe(true);
12 | });
13 |
14 | test('It should return a Nothing if the Validation is a Failure', () => {
15 | const maybe = Validation.toMaybe(Validation.Failure());
16 |
17 | expect(maybe.isNothing()).toBe(true);
18 | });
19 | });
20 |
21 | describe('Validation#toMaybe', () => {
22 | test('It should return a Just if the instance is a Success', () => {
23 | expect(Validation.Success.of().toMaybe().isJust()).toBe(true);
24 | });
25 |
26 | test('It should return a Nothing if the instance is a Failure', () => {
27 | expect(Validation.Failure.of().toMaybe().isNothing()).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/validation/toResult.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 |
6 | describe('Data.Validation', () => {
7 | describe('Validation.toResult', () => {
8 | test('It should return an Ok if the Validation is a Success', () => {
9 | const maybe = Validation.toResult(Validation.Success());
10 |
11 | expect(maybe.isOk()).toBe(true);
12 | });
13 |
14 | test('It should an Err if the Validation is a Failure', () => {
15 | const maybe = Validation.toResult(Validation.Failure());
16 |
17 | expect(maybe.isErr()).toBe(true);
18 | });
19 | });
20 |
21 | describe('Validation#toResult', () => {
22 | test('It should return an Ok if the instance is a Success', () => {
23 | expect(Validation.Success.of().toResult().isOk()).toBe(true);
24 | });
25 |
26 | test('It should return an Err if the instance is a Failure', () => {
27 | expect(Validation.Failure.of().toResult().isErr()).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/validation/toTask.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test, jest */
2 |
3 | const Validation = require('../../src/validation');
4 |
5 |
6 | describe('Data.Validation', () => {
7 | describe('Validation.toTask', () => {
8 | test('It should return a resolved task if the Validation is a Success', () => {
9 | const task = Validation.toTask(Validation.Success());
10 | const rejectedCallback = jest.fn();
11 | const resolvedCallback = jest.fn();
12 |
13 | task.fork(rejectedCallback, resolvedCallback);
14 |
15 | expect(rejectedCallback).not.toHaveBeenCalled();
16 | expect(resolvedCallback).toHaveBeenCalled();
17 | });
18 |
19 | test('It should return a rejected task if the Validation is a Failure', () => {
20 | const task = Validation.toTask(Validation.Failure());
21 | const rejectedCallback = jest.fn();
22 | const resolvedCallback = jest.fn();
23 |
24 | task.fork(rejectedCallback, resolvedCallback);
25 |
26 | expect(rejectedCallback).toHaveBeenCalled();
27 | expect(resolvedCallback).not.toHaveBeenCalled();
28 | });
29 | });
30 |
31 | describe('Validation#toTask', () => {
32 | test('It should return a resolved task if the instance is a Success', () => {
33 | const task = Validation.Success.of().toTask();
34 | const rejectedCallback = jest.fn();
35 | const resolvedCallback = jest.fn();
36 |
37 | task.fork(rejectedCallback, resolvedCallback);
38 |
39 | expect(rejectedCallback).not.toHaveBeenCalled();
40 | expect(resolvedCallback).toHaveBeenCalled();
41 | });
42 |
43 | test('It should return a rejected task if the instance is a Failure', () => {
44 | const task = Validation.Failure.of().toTask();
45 | const rejectedCallback = jest.fn();
46 | const resolvedCallback = jest.fn();
47 |
48 | task.fork(rejectedCallback, resolvedCallback);
49 |
50 | expect(rejectedCallback).toHaveBeenCalled();
51 | expect(resolvedCallback).not.toHaveBeenCalled();
52 | });
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/test/writer/andThen.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Writer = require('../../src/writer');
4 | const Tuple = require('../../src/tuple');
5 |
6 | describe('Writer', () => {
7 | describe('Writer.andThen', () => {
8 | test('it should apply the transformation and flatten the Writer that was returned', () => {
9 | const writer = Writer.of('foo');
10 | const toBaz = () => Writer.of('baz');
11 |
12 | expect(Writer.andThen(toBaz, writer)).toEqual({ value: Tuple('baz', []) });
13 | });
14 | });
15 |
16 | describe('Writer#andThen', () => {
17 | test('it should apply the transformation and flatten the Writer that was returned', () => {
18 | const writer = Writer.of('foo');
19 | const toBaz = () => Writer.of('baz');
20 |
21 | expect(writer.andThen(toBaz)).toEqual({ value: Tuple('baz', []) });
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/writer/ap.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Writer = require('../../src/writer');
4 | const Tuple = require('../../src/tuple');
5 |
6 | describe('Writer', () => {
7 | describe('Writer.ap', () => {
8 | test('it should apply a regular function in an Apply and return a new Writer with the result', () => {
9 | const writer = Writer.of('foo');
10 | const toBaz = Writer.of(() => 'baz');
11 |
12 | expect(Writer.ap(toBaz, writer)).toEqual({ value: Tuple('baz', []) });
13 | });
14 | });
15 |
16 | describe('Writer#ap', () => {
17 | test('it should apply a regular function in an Apply and return a new Writer with the result', () => {
18 | const writer = Writer.of('foo');
19 | const toBaz = Writer.of(() => 'baz');
20 |
21 | expect(writer.ap(toBaz)).toEqual({ value: Tuple('baz', []) });
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/writer/chain.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Writer = require('../../src/writer');
4 | const Tuple = require('../../src/tuple');
5 |
6 | describe('Writer', () => {
7 | describe('Writer.chain', () => {
8 | test('it should apply the transformation and flatten the Writer that was returned', () => {
9 | const writer = Writer.of('foo');
10 | const toBaz = () => Writer.of('baz');
11 |
12 | expect(Writer.chain(toBaz, writer)).toEqual({ value: Tuple('baz', []) });
13 | });
14 | });
15 |
16 | describe('Writer#chain', () => {
17 | test('it should apply the transformation and flatten the Writer that was returned', () => {
18 | const writer = Writer.of('foo');
19 | const toBaz = () => Writer.of('baz');
20 |
21 | expect(writer.chain(toBaz)).toEqual({ value: Tuple('baz', []) });
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/writer/map.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Writer = require('../../src/writer');
4 | const Tuple = require('../../src/tuple');
5 |
6 | describe('Writer', () => {
7 | describe('Writer.map', () => {
8 | test('it should apply a regular function and return a new Writer with the result', () => {
9 | const writer = Writer.of('foo');
10 | const toBaz = () => 'baz';
11 |
12 | expect(Writer.map(toBaz, writer)).toEqual({ value: Tuple('baz', []) });
13 | });
14 | });
15 |
16 | describe('Writer#map', () => {
17 | test('it should apply a regular function and return a new Writer with the result', () => {
18 | const writer = Writer.of('foo');
19 | const toBaz = () => 'baz';
20 |
21 | expect(writer.map(toBaz)).toEqual({ value: Tuple('baz', []) });
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/writer/of.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Writer = require('../../src/writer');
4 | const Tuple = require('../../src/tuple');
5 |
6 | describe('Writer', () => {
7 | describe('Writer.of', () => {
8 | test('it should create a new writer that will return the given value', () => {
9 | const writer = Writer.of('foo');
10 |
11 | expect(writer).toEqual({ value: Tuple('foo', []) });
12 | });
13 | });
14 |
15 | describe('Writer#of', () => {
16 | test('it should create a new writer that will return the given value', () => {
17 | const writer = Writer.of();
18 |
19 | expect(writer.of('foo')).toEqual({ value: Tuple('foo', []) });
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/writer/tell.test.js:
--------------------------------------------------------------------------------
1 | /* global describe, expect, test */
2 |
3 | const Writer = require('../../src/writer');
4 | const Tuple = require('../../src/tuple');
5 |
6 | describe('Writer', () => {
7 | describe('Writer.tell', () => {
8 | test('it should add a new piece of metadata', () => {
9 | const writer = Writer.of('foo');
10 |
11 | expect(Writer.tell('a', writer)).toEqual({ value: Tuple('foo', ['a']) });
12 | });
13 | });
14 |
15 | describe('Writer#tell', () => {
16 | test('it should add a new piece of metadata', () => {
17 | const writer = Writer.of('foo');
18 |
19 | expect(writer.tell('a')).toEqual({ value: Tuple('foo', ['a']) });
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------