@nlozgachev/pipelined 0.6.4
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/README.md +85 -0
- package/esm/mod.js +3 -0
- package/esm/package.json +3 -0
- package/esm/src/Composition/compose.js +3 -0
- package/esm/src/Composition/converge.js +3 -0
- package/esm/src/Composition/curry.js +42 -0
- package/esm/src/Composition/flip.js +20 -0
- package/esm/src/Composition/flow.js +8 -0
- package/esm/src/Composition/fn.js +85 -0
- package/esm/src/Composition/index.js +13 -0
- package/esm/src/Composition/juxt.js +3 -0
- package/esm/src/Composition/memoize.js +66 -0
- package/esm/src/Composition/not.js +25 -0
- package/esm/src/Composition/on.js +12 -0
- package/esm/src/Composition/pipe.js +3 -0
- package/esm/src/Composition/tap.js +33 -0
- package/esm/src/Composition/uncurry.js +32 -0
- package/esm/src/Core/Arr.js +463 -0
- package/esm/src/Core/Deferred.js +26 -0
- package/esm/src/Core/InternalTypes.js +1 -0
- package/esm/src/Core/Lens.js +98 -0
- package/esm/src/Core/Option.js +186 -0
- package/esm/src/Core/Optional.js +160 -0
- package/esm/src/Core/Reader.js +134 -0
- package/esm/src/Core/Rec.js +167 -0
- package/esm/src/Core/RemoteData.js +206 -0
- package/esm/src/Core/Result.js +164 -0
- package/esm/src/Core/Task.js +187 -0
- package/esm/src/Core/TaskOption.js +105 -0
- package/esm/src/Core/TaskResult.js +125 -0
- package/esm/src/Core/TaskValidation.js +101 -0
- package/esm/src/Core/These.js +241 -0
- package/esm/src/Core/Validation.js +214 -0
- package/esm/src/Core/index.js +15 -0
- package/esm/src/Types/Brand.js +28 -0
- package/esm/src/Types/NonEmptyList.js +14 -0
- package/esm/src/Types/index.js +2 -0
- package/package.json +61 -0
- package/script/mod.js +19 -0
- package/script/package.json +3 -0
- package/script/src/Composition/compose.js +6 -0
- package/script/src/Composition/converge.js +6 -0
- package/script/src/Composition/curry.js +48 -0
- package/script/src/Composition/flip.js +24 -0
- package/script/src/Composition/flow.js +11 -0
- package/script/src/Composition/fn.js +98 -0
- package/script/src/Composition/index.js +29 -0
- package/script/src/Composition/juxt.js +6 -0
- package/script/src/Composition/memoize.js +71 -0
- package/script/src/Composition/not.js +29 -0
- package/script/src/Composition/on.js +16 -0
- package/script/src/Composition/pipe.js +6 -0
- package/script/src/Composition/tap.js +37 -0
- package/script/src/Composition/uncurry.js +38 -0
- package/script/src/Core/Arr.js +466 -0
- package/script/src/Core/Deferred.js +29 -0
- package/script/src/Core/InternalTypes.js +2 -0
- package/script/src/Core/Lens.js +101 -0
- package/script/src/Core/Option.js +189 -0
- package/script/src/Core/Optional.js +163 -0
- package/script/src/Core/Reader.js +137 -0
- package/script/src/Core/Rec.js +170 -0
- package/script/src/Core/RemoteData.js +209 -0
- package/script/src/Core/Result.js +167 -0
- package/script/src/Core/Task.js +190 -0
- package/script/src/Core/TaskOption.js +108 -0
- package/script/src/Core/TaskResult.js +128 -0
- package/script/src/Core/TaskValidation.js +104 -0
- package/script/src/Core/These.js +244 -0
- package/script/src/Core/Validation.js +217 -0
- package/script/src/Core/index.js +31 -0
- package/script/src/Types/Brand.js +31 -0
- package/script/src/Types/NonEmptyList.js +18 -0
- package/script/src/Types/index.js +18 -0
- package/types/mod.d.ts +4 -0
- package/types/mod.d.ts.map +1 -0
- package/types/src/Composition/compose.d.ts +33 -0
- package/types/src/Composition/compose.d.ts.map +1 -0
- package/types/src/Composition/converge.d.ts +21 -0
- package/types/src/Composition/converge.d.ts.map +1 -0
- package/types/src/Composition/curry.d.ts +43 -0
- package/types/src/Composition/curry.d.ts.map +1 -0
- package/types/src/Composition/flip.d.ts +21 -0
- package/types/src/Composition/flip.d.ts.map +1 -0
- package/types/src/Composition/flow.d.ts +56 -0
- package/types/src/Composition/flow.d.ts.map +1 -0
- package/types/src/Composition/fn.d.ts +76 -0
- package/types/src/Composition/fn.d.ts.map +1 -0
- package/types/src/Composition/index.d.ts +14 -0
- package/types/src/Composition/index.d.ts.map +1 -0
- package/types/src/Composition/juxt.d.ts +18 -0
- package/types/src/Composition/juxt.d.ts.map +1 -0
- package/types/src/Composition/memoize.d.ts +46 -0
- package/types/src/Composition/memoize.d.ts.map +1 -0
- package/types/src/Composition/not.d.ts +26 -0
- package/types/src/Composition/not.d.ts.map +1 -0
- package/types/src/Composition/on.d.ts +13 -0
- package/types/src/Composition/on.d.ts.map +1 -0
- package/types/src/Composition/pipe.d.ts +56 -0
- package/types/src/Composition/pipe.d.ts.map +1 -0
- package/types/src/Composition/tap.d.ts +31 -0
- package/types/src/Composition/tap.d.ts.map +1 -0
- package/types/src/Composition/uncurry.d.ts +54 -0
- package/types/src/Composition/uncurry.d.ts.map +1 -0
- package/types/src/Core/Arr.d.ts +355 -0
- package/types/src/Core/Arr.d.ts.map +1 -0
- package/types/src/Core/Deferred.d.ts +49 -0
- package/types/src/Core/Deferred.d.ts.map +1 -0
- package/types/src/Core/InternalTypes.d.ts +20 -0
- package/types/src/Core/InternalTypes.d.ts.map +1 -0
- package/types/src/Core/Lens.d.ts +118 -0
- package/types/src/Core/Lens.d.ts.map +1 -0
- package/types/src/Core/Option.d.ts +205 -0
- package/types/src/Core/Option.d.ts.map +1 -0
- package/types/src/Core/Optional.d.ts +158 -0
- package/types/src/Core/Optional.d.ts.map +1 -0
- package/types/src/Core/Reader.d.ts +156 -0
- package/types/src/Core/Reader.d.ts.map +1 -0
- package/types/src/Core/Rec.d.ts +121 -0
- package/types/src/Core/Rec.d.ts.map +1 -0
- package/types/src/Core/RemoteData.d.ts +192 -0
- package/types/src/Core/RemoteData.d.ts.map +1 -0
- package/types/src/Core/Result.d.ts +176 -0
- package/types/src/Core/Result.d.ts.map +1 -0
- package/types/src/Core/Task.d.ts +189 -0
- package/types/src/Core/Task.d.ts.map +1 -0
- package/types/src/Core/TaskOption.d.ts +120 -0
- package/types/src/Core/TaskOption.d.ts.map +1 -0
- package/types/src/Core/TaskResult.d.ts +117 -0
- package/types/src/Core/TaskResult.d.ts.map +1 -0
- package/types/src/Core/TaskValidation.d.ts +119 -0
- package/types/src/Core/TaskValidation.d.ts.map +1 -0
- package/types/src/Core/These.d.ts +221 -0
- package/types/src/Core/These.d.ts.map +1 -0
- package/types/src/Core/Validation.d.ts +213 -0
- package/types/src/Core/Validation.d.ts.map +1 -0
- package/types/src/Core/index.d.ts +16 -0
- package/types/src/Core/index.d.ts.map +1 -0
- package/types/src/Types/Brand.d.ts +52 -0
- package/types/src/Types/Brand.d.ts.map +1 -0
- package/types/src/Types/NonEmptyList.d.ts +29 -0
- package/types/src/Types/NonEmptyList.d.ts.map +1 -0
- package/types/src/Types/index.d.ts +3 -0
- package/types/src/Types/index.d.ts.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# pipelined
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@nlozgachev/pipelined)[](https://github.com/nlozgachev/pipelined/actions/workflows/publish.yml)[](https://app.codecov.io/github/nlozgachev/pipelined)[](https://www.typescriptlang.org)[](https://deno.com)
|
|
4
|
+
|
|
5
|
+
Opinionated functional abstractions for TypeScript.
|
|
6
|
+
|
|
7
|
+
> **Note:** pipelined is pre-1.0. The API may change between minor versions until the 1.0 release.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm add @nlozgachev/pipelined
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## What is this?
|
|
14
|
+
|
|
15
|
+
A toolkit for expressing uncertainty precisely. Instead of `T | null`, `try/catch`, and loading
|
|
16
|
+
state flag soup, you get types that name every possible state and make invalid ones unrepresentable.
|
|
17
|
+
Each type comes with a consistent set of operations — `map`, `chain`, `match`, `getOrElse` — that
|
|
18
|
+
compose with `pipe` and `flow`.
|
|
19
|
+
|
|
20
|
+
No FP jargon required. You won't find `Monad`, `Functor`, or `Applicative` in the API.
|
|
21
|
+
|
|
22
|
+
## What's included?
|
|
23
|
+
|
|
24
|
+
### pipelined/core
|
|
25
|
+
|
|
26
|
+
- **`Option<A>`** — a value that may not exist; propagates absence without null checks.
|
|
27
|
+
- **`Result<E, A>`** — an operation that succeeds or fails with a typed error.
|
|
28
|
+
- **`Validation<E, A>`** — like `Result`, but accumulates every failure instead of stopping at the
|
|
29
|
+
first.
|
|
30
|
+
- **`Task<A>`** — a lazy, infallible async operation; nothing runs until called.
|
|
31
|
+
- **`TaskResult<E, A>`** — a lazy async operation that can fail with a typed error.
|
|
32
|
+
- **`TaskOption<A>`** — a lazy async operation that may produce nothing.
|
|
33
|
+
- **`TaskValidation<E, A>`** — a lazy async operation that accumulates validation errors.
|
|
34
|
+
- **`These<E, A>`** — an inclusive OR: holds an error, a value, or both at once.
|
|
35
|
+
- **`RemoteData<E, A>`** — the four states of a data fetch: `NotAsked`, `Loading`, `Failure`,
|
|
36
|
+
`Success`.
|
|
37
|
+
- **`Lens<S, A>`** — focus on a required field in a nested structure. Read, set, and modify
|
|
38
|
+
immutably.
|
|
39
|
+
- **`Optional<S, A>`** — like `Lens`, but the target may be absent (nullable fields, array indices).
|
|
40
|
+
- **`Reader<R, A>`** — a computation that depends on an environment `R`, supplied once at the
|
|
41
|
+
boundary.
|
|
42
|
+
- **`Arr`** — array utilities, data-last, returning `Option` instead of `undefined`.
|
|
43
|
+
- **`Rec`** — record utilities, data-last, with `Option`-returning key lookup.
|
|
44
|
+
|
|
45
|
+
### pipelined/types
|
|
46
|
+
|
|
47
|
+
- **`Brand<K, T>`** — nominal typing at compile time, zero runtime cost.
|
|
48
|
+
- **`NonEmptyList<A>`** — an array guaranteed to have at least one element.
|
|
49
|
+
|
|
50
|
+
### pipelined/composition
|
|
51
|
+
|
|
52
|
+
- **`pipe`**, **`flow`**, **`compose`** — function composition.
|
|
53
|
+
- **`curry`** / **`uncurry`**, **`tap`**, **`memoize`**, and other function utilities.
|
|
54
|
+
|
|
55
|
+
## Example
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { Option, Result } from "@nlozgachev/pipelined/core";
|
|
59
|
+
import { pipe } from "@nlozgachev/pipelined/composition";
|
|
60
|
+
|
|
61
|
+
// Chain nullable lookups without nested null checks
|
|
62
|
+
const city = pipe(
|
|
63
|
+
getUser(userId), // User | null
|
|
64
|
+
Option.fromNullable, // Option<User>
|
|
65
|
+
Option.chain((u) => Option.fromNullable(u.address)), // Option<Address>
|
|
66
|
+
Option.chain((a) => Option.fromNullable(a.city)), // Option<string>
|
|
67
|
+
Option.map((c) => c.toUpperCase()), // Option<string>
|
|
68
|
+
Option.getOrElse("UNKNOWN"), // string
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// Parse input and look up a record — both steps can fail
|
|
72
|
+
const record = pipe(
|
|
73
|
+
parseId(rawInput), // Result<ParseError, number>
|
|
74
|
+
Result.chain((id) => db.find(id)), // Result<ParseError | NotFoundError, Record>
|
|
75
|
+
Result.map((r) => r.name), // Result<ParseError | NotFoundError, string>
|
|
76
|
+
);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Documentation
|
|
80
|
+
|
|
81
|
+
Full guides and API reference at **[pipelined.lozgachev.dev](https://pipelined.lozgachev.dev)**.
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
BSD-3-Clause
|
package/esm/mod.js
ADDED
package/esm/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a multi-argument function into a curried function.
|
|
3
|
+
* The inverse of `uncurry`.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* const add = (a: number, b: number) => a + b;
|
|
8
|
+
* const curriedAdd = curry(add);
|
|
9
|
+
* curriedAdd(1)(2); // 3
|
|
10
|
+
*
|
|
11
|
+
* // Partial application
|
|
12
|
+
* const addTen = curriedAdd(10);
|
|
13
|
+
* addTen(5); // 15
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @see {@link uncurry} for the inverse operation
|
|
17
|
+
* @see {@link curry3} for 3-argument functions
|
|
18
|
+
* @see {@link curry4} for 4-argument functions
|
|
19
|
+
*/
|
|
20
|
+
export const curry = (f) => (a) => (b) => f(a, b);
|
|
21
|
+
/**
|
|
22
|
+
* Converts a 3-argument function into a curried function.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const add3 = (a: number, b: number, c: number) => a + b + c;
|
|
27
|
+
* const curriedAdd3 = curry3(add3);
|
|
28
|
+
* curriedAdd3(1)(2)(3); // 6
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export const curry3 = (f) => (a) => (b) => (c) => f(a, b, c);
|
|
32
|
+
/**
|
|
33
|
+
* Converts a 4-argument function into a curried function.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const add4 = (a: number, b: number, c: number, d: number) => a + b + c + d;
|
|
38
|
+
* const curriedAdd4 = curry4(add4);
|
|
39
|
+
* curriedAdd4(1)(2)(3)(4); // 10
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export const curry4 = (f) => (a) => (b) => (c) => (d) => f(a, b, c, d);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flips the order of arguments for a curried binary function.
|
|
3
|
+
* Converts a data-last function to data-first.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* // Original data-last (for pipe)
|
|
8
|
+
* pipe(
|
|
9
|
+
* Option.some(5),
|
|
10
|
+
* Option.map(n => n * 2)
|
|
11
|
+
* ); // Some(10)
|
|
12
|
+
*
|
|
13
|
+
* // Flipped to data-first
|
|
14
|
+
* const mapFirst = flip(Option.map);
|
|
15
|
+
* mapFirst(Option.some(5))(n => n * 2); // Some(10)
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @see {@link uncurry} for converting curried functions to multi-argument functions
|
|
19
|
+
*/
|
|
20
|
+
export const flip = (f) => (b) => (a) => f(a)(b);
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the value unchanged. The identity function.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* identity(42); // 42
|
|
7
|
+
* pipe(Option.some(5), Option.fold(() => 0, identity)); // 5
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export const identity = (a) => a;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a function that always returns the given value, ignoring its argument.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const always42 = constant(42);
|
|
17
|
+
* always42(); // 42
|
|
18
|
+
* [1, 2, 3].map(constant("x")); // ["x", "x", "x"]
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export const constant = (a) => () => a;
|
|
22
|
+
/** Always returns `true`. */
|
|
23
|
+
export const constTrue = () => true;
|
|
24
|
+
/** Always returns `false`. */
|
|
25
|
+
export const constFalse = () => false;
|
|
26
|
+
/** Always returns `null`. */
|
|
27
|
+
export const constNull = () => null;
|
|
28
|
+
/** Always returns `undefined`. */
|
|
29
|
+
export const constUndefined = () => undefined;
|
|
30
|
+
/** Always returns `void`. */
|
|
31
|
+
export const constVoid = () => { };
|
|
32
|
+
/**
|
|
33
|
+
* Combines two predicates with logical AND.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const isPositive = (n: number) => n > 0;
|
|
38
|
+
* const isEven = (n: number) => n % 2 === 0;
|
|
39
|
+
* const isPositiveEven = and(isPositive, isEven);
|
|
40
|
+
*
|
|
41
|
+
* isPositiveEven(4); // true
|
|
42
|
+
* isPositiveEven(-2); // false
|
|
43
|
+
* isPositiveEven(3); // false
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export const and = (p1, p2) => (...args) => p1(...args) && p2(...args);
|
|
47
|
+
/**
|
|
48
|
+
* Combines two predicates with logical OR.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const isNegative = (n: number) => n < 0;
|
|
53
|
+
* const isZero = (n: number) => n === 0;
|
|
54
|
+
* const isNonPositive = or(isNegative, isZero);
|
|
55
|
+
*
|
|
56
|
+
* isNonPositive(-1); // true
|
|
57
|
+
* isNonPositive(0); // true
|
|
58
|
+
* isNonPositive(1); // false
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export const or = (p1, p2) => (...args) => p1(...args) || p2(...args);
|
|
62
|
+
/**
|
|
63
|
+
* Creates a function that executes at most once.
|
|
64
|
+
* Subsequent calls return the cached result from the first execution.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* let count = 0;
|
|
69
|
+
* const initOnce = once(() => { count++; return "initialized"; });
|
|
70
|
+
*
|
|
71
|
+
* initOnce(); // "initialized", count === 1
|
|
72
|
+
* initOnce(); // "initialized", count === 1 (not called again)
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export const once = (f) => {
|
|
76
|
+
let called = false;
|
|
77
|
+
let result;
|
|
78
|
+
return () => {
|
|
79
|
+
if (!called) {
|
|
80
|
+
result = f();
|
|
81
|
+
called = true;
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./compose.js";
|
|
2
|
+
export * from "./converge.js";
|
|
3
|
+
export * from "./curry.js";
|
|
4
|
+
export * from "./flip.js";
|
|
5
|
+
export * from "./fn.js";
|
|
6
|
+
export * from "./flow.js";
|
|
7
|
+
export * from "./juxt.js";
|
|
8
|
+
export * from "./memoize.js";
|
|
9
|
+
export * from "./not.js";
|
|
10
|
+
export * from "./on.js";
|
|
11
|
+
export * from "./pipe.js";
|
|
12
|
+
export * from "./tap.js";
|
|
13
|
+
export * from "./uncurry.js";
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a memoized version of a function that caches results.
|
|
3
|
+
* Subsequent calls with the same argument return the cached result.
|
|
4
|
+
*
|
|
5
|
+
* By default, uses the argument directly as the cache key.
|
|
6
|
+
* For complex arguments, provide a custom `keyFn` to generate cache keys.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* // Basic usage
|
|
11
|
+
* const expensive = memoize((n: number) => {
|
|
12
|
+
* console.log("Computing...");
|
|
13
|
+
* return n * 2;
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* expensive(5); // logs "Computing...", returns 10
|
|
17
|
+
* expensive(5); // returns 10 (cached, no log)
|
|
18
|
+
* expensive(3); // logs "Computing...", returns 6
|
|
19
|
+
*
|
|
20
|
+
* // With custom key function for objects
|
|
21
|
+
* const fetchUser = memoize(
|
|
22
|
+
* (opts: { id: string }) => fetch(`/users/${opts.id}`),
|
|
23
|
+
* opts => opts.id
|
|
24
|
+
* );
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export const memoize = (f, keyFn = (a) => a) => {
|
|
28
|
+
const cache = new Map();
|
|
29
|
+
return (a) => {
|
|
30
|
+
const key = keyFn(a);
|
|
31
|
+
if (cache.has(key)) {
|
|
32
|
+
return cache.get(key);
|
|
33
|
+
}
|
|
34
|
+
const result = f(a);
|
|
35
|
+
cache.set(key, result);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Creates a memoized version of a function using WeakMap.
|
|
41
|
+
* Only works with object arguments, but allows garbage collection
|
|
42
|
+
* of cached values when keys are no longer referenced.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const processUser = memoizeWeak((user: User) => {
|
|
47
|
+
* return expensiveOperation(user);
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* const user = { id: 1, name: "Alice" };
|
|
51
|
+
* processUser(user); // computed
|
|
52
|
+
* processUser(user); // cached
|
|
53
|
+
* // When `user` is garbage collected, cached result is too
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export const memoizeWeak = (f) => {
|
|
57
|
+
const cache = new WeakMap();
|
|
58
|
+
return (a) => {
|
|
59
|
+
if (cache.has(a)) {
|
|
60
|
+
return cache.get(a);
|
|
61
|
+
}
|
|
62
|
+
const result = f(a);
|
|
63
|
+
cache.set(a, result);
|
|
64
|
+
return result;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Negates a predicate function.
|
|
3
|
+
* Returns a new predicate that returns true when the original returns false, and vice versa.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* const isEven = (n: number) => n % 2 === 0;
|
|
8
|
+
* const isOdd = not(isEven);
|
|
9
|
+
*
|
|
10
|
+
* isOdd(3); // true
|
|
11
|
+
* isOdd(4); // false
|
|
12
|
+
*
|
|
13
|
+
* // With array methods
|
|
14
|
+
* const numbers = [1, 2, 3, 4, 5];
|
|
15
|
+
* numbers.filter(not(isEven)); // [1, 3, 5]
|
|
16
|
+
*
|
|
17
|
+
* // In pipelines
|
|
18
|
+
* pipe(
|
|
19
|
+
* users,
|
|
20
|
+
* Array.filter(not(isAdmin)),
|
|
21
|
+
* Array.map(u => u.name)
|
|
22
|
+
* );
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export const not = (predicate) => (...args) => !predicate(...args);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Applies a projection to both arguments of a binary function before calling it.
|
|
3
|
+
* Most useful for building comparators and equality checks over projected values.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* const byLength = on((a: number, b: number) => a - b, (s: string) => s.length);
|
|
8
|
+
*
|
|
9
|
+
* ["banana", "fig", "apple"].sort(byLength); // ["fig", "apple", "banana"]
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export const on = (f, g) => (a, b) => f(g(a), g(b));
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Executes a side effect function and returns the original value unchanged.
|
|
3
|
+
* Useful for logging, debugging, or other side effects within a pipeline.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* // Debugging a pipeline
|
|
8
|
+
* pipe(
|
|
9
|
+
* Option.some(5),
|
|
10
|
+
* tap(x => console.log("Before map:", x)),
|
|
11
|
+
* Option.map(n => n * 2),
|
|
12
|
+
* tap(x => console.log("After map:", x)),
|
|
13
|
+
* Option.getOrElse(0)
|
|
14
|
+
* );
|
|
15
|
+
* // logs: "Before map: { kind: 'Some', value: 5 }"
|
|
16
|
+
* // logs: "After map: { kind: 'Some', value: 10 }"
|
|
17
|
+
* // returns: 10
|
|
18
|
+
*
|
|
19
|
+
* // Collecting intermediate values
|
|
20
|
+
* const values: number[] = [];
|
|
21
|
+
* pipe(
|
|
22
|
+
* [1, 2, 3],
|
|
23
|
+
* arr => arr.map(n => n * 2),
|
|
24
|
+
* tap(arr => values.push(...arr))
|
|
25
|
+
* );
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @see {@link Option.tap} for Option-specific tap that only runs on Some
|
|
29
|
+
*/
|
|
30
|
+
export const tap = (f) => (a) => {
|
|
31
|
+
f(a);
|
|
32
|
+
return a;
|
|
33
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// deno-lint-ignore no-explicit-any
|
|
2
|
+
export function uncurry(f) {
|
|
3
|
+
// f.length determines the outer arity; inner.length determines the inner arity.
|
|
4
|
+
// The typed overloads guarantee these are 0, 1, or 2 total args.
|
|
5
|
+
// deno-lint-ignore no-explicit-any
|
|
6
|
+
return (...args) => {
|
|
7
|
+
const inner = f(...args.slice(0, f.length));
|
|
8
|
+
return inner.length === 0 ? inner() : inner(...args.slice(f.length));
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Converts a curried 3-argument function into a multi-argument function.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const curriedAdd3 = (a: number) => (b: number) => (c: number) => a + b + c;
|
|
17
|
+
* const add3 = uncurry3(curriedAdd3);
|
|
18
|
+
* add3(1, 2, 3); // 6
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export const uncurry3 = (f) => (a, b, c) => f(a)(b)(c);
|
|
22
|
+
/**
|
|
23
|
+
* Converts a curried 4-argument function into a multi-argument function.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const curriedAdd4 = (a: number) => (b: number) => (c: number) => (d: number) => a + b + c + d;
|
|
28
|
+
* const add4 = uncurry4(curriedAdd4);
|
|
29
|
+
* add4(1, 2, 3, 4); // 10
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export const uncurry4 = (f) => (a, b, c, d) => f(a)(b)(c)(d);
|