@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.
Files changed (144) hide show
  1. package/README.md +85 -0
  2. package/esm/mod.js +3 -0
  3. package/esm/package.json +3 -0
  4. package/esm/src/Composition/compose.js +3 -0
  5. package/esm/src/Composition/converge.js +3 -0
  6. package/esm/src/Composition/curry.js +42 -0
  7. package/esm/src/Composition/flip.js +20 -0
  8. package/esm/src/Composition/flow.js +8 -0
  9. package/esm/src/Composition/fn.js +85 -0
  10. package/esm/src/Composition/index.js +13 -0
  11. package/esm/src/Composition/juxt.js +3 -0
  12. package/esm/src/Composition/memoize.js +66 -0
  13. package/esm/src/Composition/not.js +25 -0
  14. package/esm/src/Composition/on.js +12 -0
  15. package/esm/src/Composition/pipe.js +3 -0
  16. package/esm/src/Composition/tap.js +33 -0
  17. package/esm/src/Composition/uncurry.js +32 -0
  18. package/esm/src/Core/Arr.js +463 -0
  19. package/esm/src/Core/Deferred.js +26 -0
  20. package/esm/src/Core/InternalTypes.js +1 -0
  21. package/esm/src/Core/Lens.js +98 -0
  22. package/esm/src/Core/Option.js +186 -0
  23. package/esm/src/Core/Optional.js +160 -0
  24. package/esm/src/Core/Reader.js +134 -0
  25. package/esm/src/Core/Rec.js +167 -0
  26. package/esm/src/Core/RemoteData.js +206 -0
  27. package/esm/src/Core/Result.js +164 -0
  28. package/esm/src/Core/Task.js +187 -0
  29. package/esm/src/Core/TaskOption.js +105 -0
  30. package/esm/src/Core/TaskResult.js +125 -0
  31. package/esm/src/Core/TaskValidation.js +101 -0
  32. package/esm/src/Core/These.js +241 -0
  33. package/esm/src/Core/Validation.js +214 -0
  34. package/esm/src/Core/index.js +15 -0
  35. package/esm/src/Types/Brand.js +28 -0
  36. package/esm/src/Types/NonEmptyList.js +14 -0
  37. package/esm/src/Types/index.js +2 -0
  38. package/package.json +61 -0
  39. package/script/mod.js +19 -0
  40. package/script/package.json +3 -0
  41. package/script/src/Composition/compose.js +6 -0
  42. package/script/src/Composition/converge.js +6 -0
  43. package/script/src/Composition/curry.js +48 -0
  44. package/script/src/Composition/flip.js +24 -0
  45. package/script/src/Composition/flow.js +11 -0
  46. package/script/src/Composition/fn.js +98 -0
  47. package/script/src/Composition/index.js +29 -0
  48. package/script/src/Composition/juxt.js +6 -0
  49. package/script/src/Composition/memoize.js +71 -0
  50. package/script/src/Composition/not.js +29 -0
  51. package/script/src/Composition/on.js +16 -0
  52. package/script/src/Composition/pipe.js +6 -0
  53. package/script/src/Composition/tap.js +37 -0
  54. package/script/src/Composition/uncurry.js +38 -0
  55. package/script/src/Core/Arr.js +466 -0
  56. package/script/src/Core/Deferred.js +29 -0
  57. package/script/src/Core/InternalTypes.js +2 -0
  58. package/script/src/Core/Lens.js +101 -0
  59. package/script/src/Core/Option.js +189 -0
  60. package/script/src/Core/Optional.js +163 -0
  61. package/script/src/Core/Reader.js +137 -0
  62. package/script/src/Core/Rec.js +170 -0
  63. package/script/src/Core/RemoteData.js +209 -0
  64. package/script/src/Core/Result.js +167 -0
  65. package/script/src/Core/Task.js +190 -0
  66. package/script/src/Core/TaskOption.js +108 -0
  67. package/script/src/Core/TaskResult.js +128 -0
  68. package/script/src/Core/TaskValidation.js +104 -0
  69. package/script/src/Core/These.js +244 -0
  70. package/script/src/Core/Validation.js +217 -0
  71. package/script/src/Core/index.js +31 -0
  72. package/script/src/Types/Brand.js +31 -0
  73. package/script/src/Types/NonEmptyList.js +18 -0
  74. package/script/src/Types/index.js +18 -0
  75. package/types/mod.d.ts +4 -0
  76. package/types/mod.d.ts.map +1 -0
  77. package/types/src/Composition/compose.d.ts +33 -0
  78. package/types/src/Composition/compose.d.ts.map +1 -0
  79. package/types/src/Composition/converge.d.ts +21 -0
  80. package/types/src/Composition/converge.d.ts.map +1 -0
  81. package/types/src/Composition/curry.d.ts +43 -0
  82. package/types/src/Composition/curry.d.ts.map +1 -0
  83. package/types/src/Composition/flip.d.ts +21 -0
  84. package/types/src/Composition/flip.d.ts.map +1 -0
  85. package/types/src/Composition/flow.d.ts +56 -0
  86. package/types/src/Composition/flow.d.ts.map +1 -0
  87. package/types/src/Composition/fn.d.ts +76 -0
  88. package/types/src/Composition/fn.d.ts.map +1 -0
  89. package/types/src/Composition/index.d.ts +14 -0
  90. package/types/src/Composition/index.d.ts.map +1 -0
  91. package/types/src/Composition/juxt.d.ts +18 -0
  92. package/types/src/Composition/juxt.d.ts.map +1 -0
  93. package/types/src/Composition/memoize.d.ts +46 -0
  94. package/types/src/Composition/memoize.d.ts.map +1 -0
  95. package/types/src/Composition/not.d.ts +26 -0
  96. package/types/src/Composition/not.d.ts.map +1 -0
  97. package/types/src/Composition/on.d.ts +13 -0
  98. package/types/src/Composition/on.d.ts.map +1 -0
  99. package/types/src/Composition/pipe.d.ts +56 -0
  100. package/types/src/Composition/pipe.d.ts.map +1 -0
  101. package/types/src/Composition/tap.d.ts +31 -0
  102. package/types/src/Composition/tap.d.ts.map +1 -0
  103. package/types/src/Composition/uncurry.d.ts +54 -0
  104. package/types/src/Composition/uncurry.d.ts.map +1 -0
  105. package/types/src/Core/Arr.d.ts +355 -0
  106. package/types/src/Core/Arr.d.ts.map +1 -0
  107. package/types/src/Core/Deferred.d.ts +49 -0
  108. package/types/src/Core/Deferred.d.ts.map +1 -0
  109. package/types/src/Core/InternalTypes.d.ts +20 -0
  110. package/types/src/Core/InternalTypes.d.ts.map +1 -0
  111. package/types/src/Core/Lens.d.ts +118 -0
  112. package/types/src/Core/Lens.d.ts.map +1 -0
  113. package/types/src/Core/Option.d.ts +205 -0
  114. package/types/src/Core/Option.d.ts.map +1 -0
  115. package/types/src/Core/Optional.d.ts +158 -0
  116. package/types/src/Core/Optional.d.ts.map +1 -0
  117. package/types/src/Core/Reader.d.ts +156 -0
  118. package/types/src/Core/Reader.d.ts.map +1 -0
  119. package/types/src/Core/Rec.d.ts +121 -0
  120. package/types/src/Core/Rec.d.ts.map +1 -0
  121. package/types/src/Core/RemoteData.d.ts +192 -0
  122. package/types/src/Core/RemoteData.d.ts.map +1 -0
  123. package/types/src/Core/Result.d.ts +176 -0
  124. package/types/src/Core/Result.d.ts.map +1 -0
  125. package/types/src/Core/Task.d.ts +189 -0
  126. package/types/src/Core/Task.d.ts.map +1 -0
  127. package/types/src/Core/TaskOption.d.ts +120 -0
  128. package/types/src/Core/TaskOption.d.ts.map +1 -0
  129. package/types/src/Core/TaskResult.d.ts +117 -0
  130. package/types/src/Core/TaskResult.d.ts.map +1 -0
  131. package/types/src/Core/TaskValidation.d.ts +119 -0
  132. package/types/src/Core/TaskValidation.d.ts.map +1 -0
  133. package/types/src/Core/These.d.ts +221 -0
  134. package/types/src/Core/These.d.ts.map +1 -0
  135. package/types/src/Core/Validation.d.ts +213 -0
  136. package/types/src/Core/Validation.d.ts.map +1 -0
  137. package/types/src/Core/index.d.ts +16 -0
  138. package/types/src/Core/index.d.ts.map +1 -0
  139. package/types/src/Types/Brand.d.ts +52 -0
  140. package/types/src/Types/Brand.d.ts.map +1 -0
  141. package/types/src/Types/NonEmptyList.d.ts +29 -0
  142. package/types/src/Types/NonEmptyList.d.ts.map +1 -0
  143. package/types/src/Types/index.d.ts +3 -0
  144. package/types/src/Types/index.d.ts.map +1 -0
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # pipelined
2
+
3
+ [![npm](https://img.shields.io/npm/v/@nlozgachev/pipelined?style=for-the-badge&color=000&logo=npm&label&logoColor=fff)](https://www.npmjs.com/package/@nlozgachev/pipelined)[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/nlozgachev/pipelined/publish.yml?style=for-the-badge&color=000&logo=githubactions&label&logoColor=fff)](https://github.com/nlozgachev/pipelined/actions/workflows/publish.yml)[![Codecov](https://img.shields.io/codecov/c/github/nlozgachev/pipelined?style=for-the-badge&color=000&logo=codecov&label&logoColor=fff)](https://app.codecov.io/github/nlozgachev/pipelined)[![TypeScript](https://img.shields.io/badge/-0?style=for-the-badge&color=000&logo=typescript&label&logoColor=fff)](https://www.typescriptlang.org)[![Deno](https://img.shields.io/badge/-0?style=for-the-badge&color=000&logo=Deno&label&logoColor=fff)](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
@@ -0,0 +1,3 @@
1
+ export * from "./src/Composition/index.js";
2
+ export * from "./src/Core/index.js";
3
+ export * from "./src/Composition/index.js";
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,3 @@
1
+ export function compose(...fns) {
2
+ return (arg) => fns.reduceRight((acc, fn) => fn(acc), arg);
3
+ }
@@ -0,0 +1,3 @@
1
+ export function converge(f, transformers) {
2
+ return (a) => f(...transformers.map((t) => t(a)));
3
+ }
@@ -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,8 @@
1
+ export function flow(...fns) {
2
+ return (...args) => {
3
+ if (fns.length === 0)
4
+ return args[0];
5
+ const [first, ...rest] = fns;
6
+ return rest.reduce((acc, fn) => fn(acc), first(...args));
7
+ };
8
+ }
@@ -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,3 @@
1
+ export function juxt(fns) {
2
+ return (a) => fns.map((f) => f(a));
3
+ }
@@ -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,3 @@
1
+ export function pipe(a, ...fns) {
2
+ return fns.reduce((acc, fn) => fn(acc), a);
3
+ }
@@ -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);