onlyfuncs 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +138 -2
- package/package.json +29 -4
- package/src/function/index.js +109 -0
- package/src/index.js +10 -0
- package/src/monoid/index.js +121 -0
- package/src/option/index.js +321 -0
- package/src/predicate/index.js +72 -0
- package/src/readonly-array/index.js +136 -0
- package/src/result/index.js +455 -0
- package/src/semigroup/index.js +112 -0
- package/src/task/index.js +246 -0
- package/src/task-result/index.js +754 -0
- package/src/validation/index.js +122 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kyrylo Tykhanskyi
|
|
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.
|
package/README.md
CHANGED
|
@@ -1,3 +1,139 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="public/logo-transparent.png" alt="OnlyFuncs banner" width="700">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
# OnlyFuncs
|
|
6
|
+
|
|
7
|
+
Lightweight and hopefully ready to use library for functional programming in JavaScript.
|
|
8
|
+
It was written out of curiosity and because someone got really bored with OOP.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Description
|
|
12
|
+
|
|
13
|
+
Provides a wide set of functions and types that are quite regular for functional programming. For example map,
|
|
14
|
+
pipe, compose, result, option, traverse etc. So if you want to give FP a shot, save yourself some time and try
|
|
15
|
+
the lib. It doesn't bite. Mostly.
|
|
16
|
+
|
|
17
|
+
Also, it does its best to be readable and supports asynchronous operations.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install onlyfuncs
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
Import the functions you need from the library module:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import { pipe } from "onlyfuncs/function";
|
|
33
|
+
import * as Option from "onlyfuncs/option";
|
|
34
|
+
import * as Result from "onlyfuncs/result";
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or directly from the root:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
import { Option, Result } from "onlyfuncs";
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Use them in chains:
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
const racer = (name) => pipe(
|
|
47
|
+
Option.fromNullable(name),
|
|
48
|
+
Option.map((name) => name.trim()),
|
|
49
|
+
Option.filter((name) => name.length > 0),
|
|
50
|
+
Option.getOrElse(() => "Luigi")
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
console.log(racer(" Mario ")) // Mario
|
|
54
|
+
console.log(racer(" ")) // Luigi
|
|
55
|
+
console.log(racer()) // Luigi
|
|
56
|
+
console.log(racer("Bowser")) // Bowser
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Reach out to Result if you want to check why operation failed:
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
const racer = (value) => pipe(
|
|
63
|
+
Result.fromThrowable(() => JSON.parse(value)),
|
|
64
|
+
Result.mapError((_) => "invalid JSON"),
|
|
65
|
+
Result.map((user) => user?.name),
|
|
66
|
+
Result.flatMap((name) => typeof name === "string" && name.trim().length > 0
|
|
67
|
+
? Result.ok(name.trim())
|
|
68
|
+
: Result.err("name is missing")),
|
|
69
|
+
Result.match({
|
|
70
|
+
onOk: (name) => `Select: ${name}`,
|
|
71
|
+
onErr: (err) => `Could not choose a racer: ${err}`
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
console.log(racer('{"name": "Mario"}')); // Mario
|
|
77
|
+
console.log(racer('{"na": "Luigi"}')); // Name is missing
|
|
78
|
+
console.log(racer('{')); // Invalid JSON
|
|
79
|
+
console.log(racer('{"name": "Bowser", "power": "fireball"}')); // Bowser
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Reach out to TaskResult for running async operations that may fail:
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
import { pipe } from "onlyfuncs/function";
|
|
86
|
+
import * as TaskResult from "onlyfuncs/task-result";
|
|
87
|
+
|
|
88
|
+
const lookupItem = async (name) => {
|
|
89
|
+
const response = await fetch(
|
|
90
|
+
`https://dummyjson.com/products/search?q=${encodeURIComponent(name)}`,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
throw new Error(`Request failed: ${response.status}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return await response.json();
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const item = (name) => pipe(
|
|
101
|
+
TaskResult.fromThrowable(() => lookupItem(name)),
|
|
102
|
+
TaskResult.mapError((err) => err.message),
|
|
103
|
+
TaskResult.map((data) => data.products[0]),
|
|
104
|
+
TaskResult.filterOrElse({
|
|
105
|
+
predicate: (item) => item !== undefined,
|
|
106
|
+
onFalse: () => `${name} was not found`,
|
|
107
|
+
}),
|
|
108
|
+
TaskResult.map((item) => item.title),
|
|
109
|
+
TaskResult.match({
|
|
110
|
+
onOk: (title) => `Gotcha ${title}`,
|
|
111
|
+
onErr: (err) => `Could not get item: ${err}`,
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
console.log(await item("cup")());
|
|
116
|
+
console.log(await item("apple")());
|
|
117
|
+
console.log(await item("moonstone")());
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
## Modules
|
|
122
|
+
|
|
123
|
+
| Module | Import | Purpose |
|
|
124
|
+
| --- | --- | --- |
|
|
125
|
+
| Function | `onlyfuncs/function` | Compose and transform functions with `pipe`, `flow`, and `compose`|
|
|
126
|
+
| Option | `onlyfuncs/option` | Represent optional values and handle missing data|
|
|
127
|
+
| Result | `onlyfuncs/result` | Represent success or failure and recover from errors|
|
|
128
|
+
| Predicate | `onlyfuncs/predicate` | Combine and adapt boolean checks |
|
|
129
|
+
| ReadonlyArray | `onlyfuncs/readonly-array` | Find, group, and transform array values |
|
|
130
|
+
| Task | `onlyfuncs/task` | Define lazy asynchronous computations |
|
|
131
|
+
| TaskResult | `onlyfuncs/task-result` | Combine asynchronous computations with success and failure handling |
|
|
132
|
+
| Semigroup | `onlyfuncs/semigroup` | Combine values, such as strings, arrays, and records |
|
|
133
|
+
| Monoid | `onlyfuncs/monoid` | Combine values with an identity |
|
|
134
|
+
| Validation | `onlyfuncs/validation` | Accumulate errors from independent checks |
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
MIT © 2026 Kyrylo Tykhanskyi. See [LICENSE](LICENSE).
|
package/package.json
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "onlyfuncs",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"description": "Lightweight library for functional programming",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": "https://github.com/devkyt/onlyfuncs",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "node --test"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src/index.js",
|
|
13
|
+
"src/*/index.js"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": "./src/index.js",
|
|
17
|
+
"./function": "./src/function/index.js",
|
|
18
|
+
"./option": "./src/option/index.js",
|
|
19
|
+
"./result": "./src/result/index.js",
|
|
20
|
+
"./predicate": "./src/predicate/index.js",
|
|
21
|
+
"./readonly-array": "./src/readonly-array/index.js",
|
|
22
|
+
"./task": "./src/task/index.js",
|
|
23
|
+
"./task-result": "./src/task-result/index.js",
|
|
24
|
+
"./semigroup": "./src/semigroup/index.js",
|
|
25
|
+
"./monoid": "./src/monoid/index.js",
|
|
26
|
+
"./validation": "./src/validation/index.js"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"registry": "https://registry.npmjs.org/"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the input unchanged, preserving object references.
|
|
3
|
+
*
|
|
4
|
+
* @template A
|
|
5
|
+
* @param {A} value - The value to return.
|
|
6
|
+
* @returns {A} The original value.
|
|
7
|
+
* @example
|
|
8
|
+
* identity("Link"); // "Link"
|
|
9
|
+
*/
|
|
10
|
+
export const identity = (value) => value;
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates a function that returns the same captured value on every call.
|
|
15
|
+
* Object references are preserved.
|
|
16
|
+
*
|
|
17
|
+
* @template A
|
|
18
|
+
* @param {A} value - The value to capture.
|
|
19
|
+
* @returns {() => A} The constant function.
|
|
20
|
+
* @example
|
|
21
|
+
* const hero = constant("Link");
|
|
22
|
+
* hero(); // "Link"
|
|
23
|
+
*/
|
|
24
|
+
export const constant = (value) => () => value;
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Immediately passes a value through steps from left to right.
|
|
29
|
+
* Each step receives only the previous result. With no steps, returns the input.
|
|
30
|
+
* Promises are passed along without awaiting. A throw stops the remaining steps.
|
|
31
|
+
*
|
|
32
|
+
* @param {any} value - The input passed to the first step.
|
|
33
|
+
* @param {...((value: any) => any)} functions - Steps to apply in order.
|
|
34
|
+
* @returns {any} The final value, or the input if no steps are given.
|
|
35
|
+
* @throws {TypeError} When a non-function step is reached.
|
|
36
|
+
* @example
|
|
37
|
+
* pipe(3, n => n + 1, n => n * 2); // 8
|
|
38
|
+
*/
|
|
39
|
+
export const pipe = (value, ...functions) => {
|
|
40
|
+
const apply = (value, fn) => {
|
|
41
|
+
if (typeof fn !=='function') {
|
|
42
|
+
throw new TypeError('pipe: expected all arguments to be functions');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return fn(value)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return functions.reduce((acc, fn) => apply(acc, fn), value);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Creates a function that passes its input through steps from left to right.
|
|
53
|
+
* Each step receives only the previous result. With no steps, returns the input.
|
|
54
|
+
* Promises are passed along without awaiting. A throw stops the remaining steps.
|
|
55
|
+
*
|
|
56
|
+
* @param {...((value: any) => any)} functions - Steps to apply in order.
|
|
57
|
+
* @returns {(value: any) => any} The composed function.
|
|
58
|
+
* @throws {TypeError} When the returned function reaches a non-function step.
|
|
59
|
+
* @example
|
|
60
|
+
* const calculate = flow(n => n + 1, n => n * 2);
|
|
61
|
+
* calculate(3); // 8
|
|
62
|
+
*/
|
|
63
|
+
export const flow = (...functions) => (value) => pipe(value, ...functions);
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Creates a function that passes its input through steps from right to left.
|
|
68
|
+
* Each step receives only the previous result. With no steps, returns the input.
|
|
69
|
+
* Promises are passed along without awaiting. A throw stops the remaining steps.
|
|
70
|
+
*
|
|
71
|
+
* @param {...((value: any) => any)} functions - Steps to apply in reverse order.
|
|
72
|
+
* @returns {(value: any) => any} The composed function.
|
|
73
|
+
* @throws {TypeError} When the returned function reaches a non-function step.
|
|
74
|
+
* @example
|
|
75
|
+
* const calculate = compose(n => n * 2, n => n + 1);
|
|
76
|
+
* calculate(3); // 8
|
|
77
|
+
*/
|
|
78
|
+
export const compose = (...functions) => flow(...functions.reverse());
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Converts a two-argument function into two successive one-argument calls.
|
|
83
|
+
* Calls fn after receiving the second argument, preserving argument order.
|
|
84
|
+
* Exceptions from fn propagate.
|
|
85
|
+
*
|
|
86
|
+
* @template A, B, R
|
|
87
|
+
* @param {(first: A, second: B) => R} fn - The two-argument function.
|
|
88
|
+
* @returns {(first: A) => (second: B) => R} The curried function.
|
|
89
|
+
* @example
|
|
90
|
+
* const subtract = curry2((a, b) => a - b);
|
|
91
|
+
* subtract(10)(3); // 7
|
|
92
|
+
*/
|
|
93
|
+
export const curry2 = (fn) => (x) => (y) => fn(x, y);
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Converts a three-argument function into three successive one-argument calls.
|
|
98
|
+
* Calls fn after receiving the third argument, preserving argument order.
|
|
99
|
+
* Exceptions from fn propagate.
|
|
100
|
+
*
|
|
101
|
+
* @template A, B, C, R
|
|
102
|
+
* @param {(first: A, second: B, third: C) => R} fn - The three-argument function.
|
|
103
|
+
* @returns {(first: A) => (second: B) => (third: C) => R} The curried function.
|
|
104
|
+
* @example
|
|
105
|
+
* const cast = curry3((caster, spell, target) => `${caster}: ${spell} on ${target}`);
|
|
106
|
+
* cast("Rauru")("Blizzard")("Goblin"); // "Rauru: Blizzard on Goblin"
|
|
107
|
+
*/
|
|
108
|
+
export const curry3 = (fn) => (x) => (y) => (z) => fn(x, y, z);
|
|
109
|
+
|
package/src/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * as Function from "./function/index.js";
|
|
2
|
+
export * as Option from "./option/index.js";
|
|
3
|
+
export * as Result from "./result/index.js";
|
|
4
|
+
export * as Predicate from "./predicate/index.js";
|
|
5
|
+
export * as ReadonlyArray from "./readonly-array/index.js";
|
|
6
|
+
export * as Task from "./task/index.js";
|
|
7
|
+
export * as TaskResult from "./task-result/index.js";
|
|
8
|
+
export * as Semigroup from "./semigroup/index.js";
|
|
9
|
+
export * as Monoid from "./monoid/index.js";
|
|
10
|
+
export * as Validation from "./validation/index.js";
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as Semigroup from "../semigroup/index.js"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* An associative combination operation with an identity value.
|
|
5
|
+
* Combining a value with the identity on either side should preserve that value.
|
|
6
|
+
* Custom combiners should preserve inputs, and empty() should create a fresh identity
|
|
7
|
+
* when it is mutable. JavaScript number rounding can limit numeric associativity.
|
|
8
|
+
*
|
|
9
|
+
* @template A
|
|
10
|
+
* @typedef {Object} Monoid
|
|
11
|
+
* @property {(left: A, right: A) => A} combine - Combines the left and right values in that order.
|
|
12
|
+
* @property {() => A} empty - Supplies the identity for the combination operation.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Combines numbers by addition, with zero as the identity.
|
|
17
|
+
* Uses JavaScript number arithmetic.
|
|
18
|
+
*
|
|
19
|
+
* @type {Monoid<number>}
|
|
20
|
+
* @example
|
|
21
|
+
* sum.combine(sum.empty(), 3); // 3
|
|
22
|
+
*/
|
|
23
|
+
export const sum = {
|
|
24
|
+
combine: Semigroup.sum.combine,
|
|
25
|
+
empty: () => 0,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Combines numbers by multiplication, with one as the identity.
|
|
31
|
+
* Uses JavaScript number arithmetic.
|
|
32
|
+
*
|
|
33
|
+
* @type {Monoid<number>}
|
|
34
|
+
* @example
|
|
35
|
+
* product.combine(product.empty(), 3); // 3
|
|
36
|
+
*/
|
|
37
|
+
export const product = {
|
|
38
|
+
combine: Semigroup.product.combine,
|
|
39
|
+
empty: () => 1,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Concatenates strings in left-to-right order, with the empty string as the identity.
|
|
45
|
+
*
|
|
46
|
+
* @type {Monoid<string>}
|
|
47
|
+
* @example
|
|
48
|
+
* string.combine(string.empty(), "Link"); // "Link"
|
|
49
|
+
*/
|
|
50
|
+
export const string = {
|
|
51
|
+
combine: Semigroup.string.combine,
|
|
52
|
+
empty: () => "",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Concatenates arrays without changing them; empty() creates a new empty array.
|
|
58
|
+
* Each combination creates a new array, preserving the elements' references.
|
|
59
|
+
*
|
|
60
|
+
* @type {{
|
|
61
|
+
* combine: <A, B>(left: ReadonlyArray<A>, right: ReadonlyArray<B>) => (A | B)[],
|
|
62
|
+
* empty: <A>() => A[]
|
|
63
|
+
* }}
|
|
64
|
+
* @example
|
|
65
|
+
* array.combine(array.empty(), ["Link"]); // ["Link"]
|
|
66
|
+
* array.empty() === array.empty(); // false
|
|
67
|
+
*/
|
|
68
|
+
export const array = {
|
|
69
|
+
combine: Semigroup.array.combine,
|
|
70
|
+
empty: () => [],
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Creates a monoid that combines matching fields into a new object.
|
|
76
|
+
* Captures the schema's own enumerable string keys and monoid references at creation.
|
|
77
|
+
* Ignores extra input fields. Each empty() call creates a new object and calls each
|
|
78
|
+
* field's empty() once; field monoids control freshness of their values.
|
|
79
|
+
* Field operations run in schema order; exceptions propagate.
|
|
80
|
+
*
|
|
81
|
+
* @template {Record<string, any>} A
|
|
82
|
+
* @param {{[K in keyof A]: Monoid<A[K]>}} fields - A monoid for each field to combine.
|
|
83
|
+
* @returns {Monoid<A>} The record monoid.
|
|
84
|
+
* @example
|
|
85
|
+
* const stats = struct({ health: sum, inventory: array });
|
|
86
|
+
* stats.empty(); // { health: 0, inventory: [] }
|
|
87
|
+
* stats.combine(stats.empty(), { health: 3, inventory: ["Bow"] });
|
|
88
|
+
* // { health: 3, inventory: ["Bow"] }
|
|
89
|
+
*/
|
|
90
|
+
export const struct = (fields) => {
|
|
91
|
+
const entries = Object.entries(fields);
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
combine: Semigroup.struct(fields).combine,
|
|
95
|
+
empty: () => Object.fromEntries(
|
|
96
|
+
entries.map(([key, monoid]) => [
|
|
97
|
+
key,
|
|
98
|
+
monoid.empty()
|
|
99
|
+
])
|
|
100
|
+
),
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Combines a finite iterable left to right, starting from the monoid's identity.
|
|
107
|
+
* Calls empty() once when values are supplied, including for empty input.
|
|
108
|
+
* Iteration, empty(), and combiner exceptions propagate.
|
|
109
|
+
*
|
|
110
|
+
* @template A
|
|
111
|
+
* @param {Monoid<A>} monoid - Supplies the identity and combines the accumulator with each element.
|
|
112
|
+
* @returns {(values: Iterable<A>) => A} A function combining a finite iterable.
|
|
113
|
+
* @example
|
|
114
|
+
* combineAll(sum)([2, 3, 4]); // 9
|
|
115
|
+
* combineAll(sum)([]); // 0
|
|
116
|
+
*/
|
|
117
|
+
export const combineAll = (monoid) => (values) =>
|
|
118
|
+
Semigroup.combineMany({ semigroup: monoid, initial: monoid.empty() })(values);
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|