effect 3.16.13 → 3.16.14

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 (67) hide show
  1. package/dist/cjs/Predicate.js +516 -200
  2. package/dist/cjs/Predicate.js.map +1 -1
  3. package/dist/cjs/Schema.js +2 -2
  4. package/dist/cjs/Schema.js.map +1 -1
  5. package/dist/cjs/internal/channel.js +2 -2
  6. package/dist/cjs/internal/channel.js.map +1 -1
  7. package/dist/cjs/internal/core-effect.js +1 -0
  8. package/dist/cjs/internal/core-effect.js.map +1 -1
  9. package/dist/cjs/internal/dataSource.js +2 -2
  10. package/dist/cjs/internal/dataSource.js.map +1 -1
  11. package/dist/cjs/internal/fiberRuntime.js +1 -1
  12. package/dist/cjs/internal/fiberRuntime.js.map +1 -1
  13. package/dist/cjs/internal/groupBy.js +2 -2
  14. package/dist/cjs/internal/groupBy.js.map +1 -1
  15. package/dist/cjs/internal/sink.js +4 -4
  16. package/dist/cjs/internal/sink.js.map +1 -1
  17. package/dist/cjs/internal/stm/stm.js +3 -3
  18. package/dist/cjs/internal/stm/stm.js.map +1 -1
  19. package/dist/cjs/internal/stream.js +5 -5
  20. package/dist/cjs/internal/stream.js.map +1 -1
  21. package/dist/cjs/internal/version.js +1 -1
  22. package/dist/dts/Effect.d.ts +1 -1
  23. package/dist/dts/Effect.d.ts.map +1 -1
  24. package/dist/dts/Predicate.d.ts +1190 -375
  25. package/dist/dts/Predicate.d.ts.map +1 -1
  26. package/dist/dts/index.d.ts +15 -0
  27. package/dist/dts/index.d.ts.map +1 -1
  28. package/dist/dts/internal/core-effect.d.ts.map +1 -1
  29. package/dist/dts/internal/stm/stm.d.ts.map +1 -1
  30. package/dist/dts/internal/stream.d.ts.map +1 -1
  31. package/dist/esm/Predicate.js +516 -200
  32. package/dist/esm/Predicate.js.map +1 -1
  33. package/dist/esm/Schema.js +2 -2
  34. package/dist/esm/Schema.js.map +1 -1
  35. package/dist/esm/index.js +15 -0
  36. package/dist/esm/index.js.map +1 -1
  37. package/dist/esm/internal/channel.js +2 -2
  38. package/dist/esm/internal/channel.js.map +1 -1
  39. package/dist/esm/internal/core-effect.js +1 -0
  40. package/dist/esm/internal/core-effect.js.map +1 -1
  41. package/dist/esm/internal/dataSource.js +2 -2
  42. package/dist/esm/internal/dataSource.js.map +1 -1
  43. package/dist/esm/internal/fiberRuntime.js +1 -1
  44. package/dist/esm/internal/fiberRuntime.js.map +1 -1
  45. package/dist/esm/internal/groupBy.js +2 -2
  46. package/dist/esm/internal/groupBy.js.map +1 -1
  47. package/dist/esm/internal/sink.js +4 -4
  48. package/dist/esm/internal/sink.js.map +1 -1
  49. package/dist/esm/internal/stm/stm.js +3 -3
  50. package/dist/esm/internal/stm/stm.js.map +1 -1
  51. package/dist/esm/internal/stream.js +5 -5
  52. package/dist/esm/internal/stream.js.map +1 -1
  53. package/dist/esm/internal/version.js +1 -1
  54. package/package.json +1 -1
  55. package/src/Effect.ts +1 -1
  56. package/src/Predicate.ts +1213 -377
  57. package/src/Schema.ts +2 -2
  58. package/src/index.ts +15 -0
  59. package/src/internal/channel.ts +2 -2
  60. package/src/internal/core-effect.ts +1 -0
  61. package/src/internal/dataSource.ts +12 -14
  62. package/src/internal/fiberRuntime.ts +2 -2
  63. package/src/internal/groupBy.ts +12 -14
  64. package/src/internal/sink.ts +13 -15
  65. package/src/internal/stm/stm.ts +16 -20
  66. package/src/internal/stream.ts +17 -23
  67. package/src/internal/version.ts +1 -1
@@ -1,21 +1,43 @@
1
1
  /**
2
+ * This module provides a collection of functions for working with predicates and refinements.
3
+ *
4
+ * A `Predicate<A>` is a function that takes a value of type `A` and returns a boolean.
5
+ * It is used to check if a value satisfies a certain condition.
6
+ *
7
+ * A `Refinement<A, B>` is a special type of predicate that not only checks a condition
8
+ * but also provides a type guard, allowing TypeScript to narrow the type of the input
9
+ * value from `A` to a more specific type `B` within a conditional block.
10
+ *
11
+ * The module includes:
12
+ * - Basic predicates and refinements for common types (e.g., `isString`, `isNumber`).
13
+ * - Combinators to create new predicates from existing ones (e.g., `and`, `or`, `not`).
14
+ * - Advanced combinators for working with data structures (e.g., `tuple`, `struct`).
15
+ * - Type-level utilities for inspecting predicate and refinement types.
16
+ *
2
17
  * @since 2.0.0
3
18
  */
4
19
  import { dual, isFunction as isFunction_ } from "./Function.js";
5
20
  /**
6
- * Given a `Predicate<A>` returns a `Predicate<B>`
21
+ * Transforms a `Predicate<A>` into a `Predicate<B>` by applying a function `(b: B) => A`
22
+ * to the input before passing it to the predicate. This is also known as "contramap" or
23
+ * "pre-composition".
7
24
  *
8
25
  * @example
9
26
  * ```ts
10
- * import * as assert from "node:assert"
11
27
  * import { Predicate, Number } from "effect"
28
+ * import * as assert from "node:assert"
29
+ *
30
+ * // A predicate on numbers
31
+ * const isPositive: Predicate.Predicate<number> = Number.greaterThan(0)
12
32
  *
13
- * const minLength3 = Predicate.mapInput(Number.greaterThan(2), (s: string) => s.length)
33
+ * // A function from `string` to `number`
34
+ * const stringLength = (s: string): number => s.length
14
35
  *
15
- * assert.deepStrictEqual(minLength3("a"), false)
16
- * assert.deepStrictEqual(minLength3("aa"), false)
17
- * assert.deepStrictEqual(minLength3("aaa"), true)
18
- * assert.deepStrictEqual(minLength3("aaaa"), true)
36
+ * // Create a new predicate on strings by mapping the input
37
+ * const hasPositiveLength = Predicate.mapInput(isPositive, stringLength)
38
+ *
39
+ * assert.strictEqual(hasPositiveLength("hello"), true)
40
+ * assert.strictEqual(hasPositiveLength(""), false)
19
41
  * ```
20
42
  *
21
43
  * @category combinators
@@ -23,23 +45,24 @@ import { dual, isFunction as isFunction_ } from "./Function.js";
23
45
  */
24
46
  export const mapInput = /*#__PURE__*/dual(2, (self, f) => b => self(f(b)));
25
47
  /**
26
- * Determine if an `Array` is a tuple with exactly `N` elements, narrowing down the type to `TupleOf`.
27
- *
28
- * An `Array` is considered to be a `TupleOf` if its length is exactly `N`.
48
+ * A refinement that checks if a `ReadonlyArray<T>` is a tuple with exactly `N` elements.
49
+ * If the check is successful, the type is narrowed to `TupleOf<N, T>`.
29
50
  *
30
51
  * @example
31
52
  * ```ts
32
53
  * import * as assert from "node:assert"
33
54
  * import { isTupleOf } from "effect/Predicate"
34
55
  *
35
- * assert.deepStrictEqual(isTupleOf([1, 2, 3], 3), true);
36
- * assert.deepStrictEqual(isTupleOf([1, 2, 3], 2), false);
37
- * assert.deepStrictEqual(isTupleOf([1, 2, 3], 4), false);
56
+ * const isTupleOf3 = isTupleOf(3)
57
+ *
58
+ * assert.strictEqual(isTupleOf3([1, 2, 3]), true);
59
+ * assert.strictEqual(isTupleOf3([1, 2]), false);
38
60
  *
39
61
  * const arr: number[] = [1, 2, 3];
40
62
  * if (isTupleOf(arr, 3)) {
41
- * console.log(arr);
42
- * // ^? [number, number, number]
63
+ * // The type of arr is now [number, number, number]
64
+ * const [a, b, c] = arr;
65
+ * assert.deepStrictEqual([a, b, c], [1, 2, 3])
43
66
  * }
44
67
  * ```
45
68
  *
@@ -48,23 +71,25 @@ export const mapInput = /*#__PURE__*/dual(2, (self, f) => b => self(f(b)));
48
71
  */
49
72
  export const isTupleOf = /*#__PURE__*/dual(2, (self, n) => self.length === n);
50
73
  /**
51
- * Determine if an `Array` is a tuple with at least `N` elements, narrowing down the type to `TupleOfAtLeast`.
52
- *
53
- * An `Array` is considered to be a `TupleOfAtLeast` if its length is at least `N`.
74
+ * A refinement that checks if a `ReadonlyArray<T>` is a tuple with at least `N` elements.
75
+ * If the check is successful, the type is narrowed to `TupleOfAtLeast<N, T>`.
54
76
  *
55
77
  * @example
56
78
  * ```ts
57
79
  * import * as assert from "node:assert"
58
80
  * import { isTupleOfAtLeast } from "effect/Predicate"
59
81
  *
60
- * assert.deepStrictEqual(isTupleOfAtLeast([1, 2, 3], 3), true);
61
- * assert.deepStrictEqual(isTupleOfAtLeast([1, 2, 3], 2), true);
62
- * assert.deepStrictEqual(isTupleOfAtLeast([1, 2, 3], 4), false);
82
+ * const isTupleOfAtLeast3 = isTupleOfAtLeast(3)
83
+ *
84
+ * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3]), true);
85
+ * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3, 4]), true);
86
+ * assert.strictEqual(isTupleOfAtLeast3([1, 2]), false);
63
87
  *
64
88
  * const arr: number[] = [1, 2, 3, 4];
65
89
  * if (isTupleOfAtLeast(arr, 3)) {
66
- * console.log(arr);
67
- * // ^? [number, number, number, ...number[]]
90
+ * // The type of arr is now [number, number, number, ...number[]]
91
+ * const [a, b, c] = arr;
92
+ * assert.deepStrictEqual([a, b, c], [1, 2, 3])
68
93
  * }
69
94
  * ```
70
95
  *
@@ -73,16 +98,22 @@ export const isTupleOf = /*#__PURE__*/dual(2, (self, n) => self.length === n);
73
98
  */
74
99
  export const isTupleOfAtLeast = /*#__PURE__*/dual(2, (self, n) => self.length >= n);
75
100
  /**
76
- * Tests if a value is `truthy`.
101
+ * A predicate that checks if a value is "truthy" in JavaScript.
102
+ * Fails for `false`, `0`, `-0`, `0n`, `""`, `null`, `undefined`, and `NaN`.
77
103
  *
78
104
  * @example
79
105
  * ```ts
80
106
  * import * as assert from "node:assert"
81
107
  * import { isTruthy } from "effect/Predicate"
82
108
  *
83
- * assert.deepStrictEqual(isTruthy(1), true)
84
- * assert.deepStrictEqual(isTruthy(0), false)
85
- * assert.deepStrictEqual(isTruthy(""), false)
109
+ * assert.strictEqual(isTruthy(1), true)
110
+ * assert.strictEqual(isTruthy("hello"), true)
111
+ * assert.strictEqual(isTruthy({}), true)
112
+ *
113
+ * assert.strictEqual(isTruthy(0), false)
114
+ * assert.strictEqual(isTruthy(""), false)
115
+ * assert.strictEqual(isTruthy(null), false)
116
+ * assert.strictEqual(isTruthy(undefined), false)
86
117
  * ```
87
118
  *
88
119
  * @category guards
@@ -90,18 +121,18 @@ export const isTupleOfAtLeast = /*#__PURE__*/dual(2, (self, n) => self.length >=
90
121
  */
91
122
  export const isTruthy = input => !!input;
92
123
  /**
93
- * Tests if a value is a `Set`.
124
+ * A refinement that checks if a value is a `Set`.
94
125
  *
95
126
  * @example
96
127
  * ```ts
97
128
  * import * as assert from "node:assert"
98
129
  * import { isSet } from "effect/Predicate"
99
130
  *
100
- * assert.deepStrictEqual(isSet(new Set([1, 2])), true)
101
- * assert.deepStrictEqual(isSet(new Set()), true)
102
- * assert.deepStrictEqual(isSet({}), false)
103
- * assert.deepStrictEqual(isSet(null), false)
104
- * assert.deepStrictEqual(isSet(undefined), false)
131
+ * assert.strictEqual(isSet(new Set([1, 2])), true)
132
+ * assert.strictEqual(isSet(new Set()), true)
133
+ *
134
+ * assert.strictEqual(isSet({}), false)
135
+ * assert.strictEqual(isSet([1, 2]), false)
105
136
  * ```
106
137
  *
107
138
  * @category guards
@@ -109,17 +140,17 @@ export const isTruthy = input => !!input;
109
140
  */
110
141
  export const isSet = input => input instanceof Set;
111
142
  /**
112
- * Tests if a value is a `Map`.
143
+ * A refinement that checks if a value is a `Map`.
113
144
  *
114
145
  * @example
115
146
  * ```ts
116
147
  * import * as assert from "node:assert"
117
148
  * import { isMap } from "effect/Predicate"
118
149
  *
119
- * assert.deepStrictEqual(isMap(new Map()), true)
120
- * assert.deepStrictEqual(isMap({}), false)
121
- * assert.deepStrictEqual(isMap(null), false)
122
- * assert.deepStrictEqual(isMap(undefined), false)
150
+ * assert.strictEqual(isMap(new Map()), true)
151
+ *
152
+ * assert.strictEqual(isMap({}), false)
153
+ * assert.strictEqual(isMap(new Set()), false)
123
154
  * ```
124
155
  *
125
156
  * @category guards
@@ -127,16 +158,18 @@ export const isSet = input => input instanceof Set;
127
158
  */
128
159
  export const isMap = input => input instanceof Map;
129
160
  /**
130
- * Tests if a value is a `string`.
161
+ * A refinement that checks if a value is a `string`.
131
162
  *
132
163
  * @example
133
164
  * ```ts
134
165
  * import * as assert from "node:assert"
135
166
  * import { isString } from "effect/Predicate"
136
167
  *
137
- * assert.deepStrictEqual(isString("a"), true)
168
+ * assert.strictEqual(isString("hello"), true)
169
+ * assert.strictEqual(isString(""), true)
138
170
  *
139
- * assert.deepStrictEqual(isString(1), false)
171
+ * assert.strictEqual(isString(123), false)
172
+ * assert.strictEqual(isString(null), false)
140
173
  * ```
141
174
  *
142
175
  * @category guards
@@ -144,16 +177,20 @@ export const isMap = input => input instanceof Map;
144
177
  */
145
178
  export const isString = input => typeof input === "string";
146
179
  /**
147
- * Tests if a value is a `number`.
180
+ * A refinement that checks if a value is a `number`. Note that this
181
+ * check returns `false` for `NaN`.
148
182
  *
149
183
  * @example
150
184
  * ```ts
151
185
  * import * as assert from "node:assert"
152
186
  * import { isNumber } from "effect/Predicate"
153
187
  *
154
- * assert.deepStrictEqual(isNumber(2), true)
188
+ * assert.strictEqual(isNumber(123), true)
189
+ * assert.strictEqual(isNumber(0), true)
190
+ * assert.strictEqual(isNumber(-1.5), true)
155
191
  *
156
- * assert.deepStrictEqual(isNumber("2"), false)
192
+ * assert.strictEqual(isNumber("123"), false)
193
+ * assert.strictEqual(isNumber(NaN), false) // Special case: NaN is a number type but returns false
157
194
  * ```
158
195
  *
159
196
  * @category guards
@@ -161,16 +198,18 @@ export const isString = input => typeof input === "string";
161
198
  */
162
199
  export const isNumber = input => typeof input === "number";
163
200
  /**
164
- * Tests if a value is a `boolean`.
201
+ * A refinement that checks if a value is a `boolean`.
165
202
  *
166
203
  * @example
167
204
  * ```ts
168
205
  * import * as assert from "node:assert"
169
206
  * import { isBoolean } from "effect/Predicate"
170
207
  *
171
- * assert.deepStrictEqual(isBoolean(true), true)
208
+ * assert.strictEqual(isBoolean(true), true)
209
+ * assert.strictEqual(isBoolean(false), true)
172
210
  *
173
- * assert.deepStrictEqual(isBoolean("true"), false)
211
+ * assert.strictEqual(isBoolean("true"), false)
212
+ * assert.strictEqual(isBoolean(0), false)
174
213
  * ```
175
214
  *
176
215
  * @category guards
@@ -178,16 +217,17 @@ export const isNumber = input => typeof input === "number";
178
217
  */
179
218
  export const isBoolean = input => typeof input === "boolean";
180
219
  /**
181
- * Tests if a value is a `bigint`.
220
+ * A refinement that checks if a value is a `bigint`.
182
221
  *
183
222
  * @example
184
223
  * ```ts
185
224
  * import * as assert from "node:assert"
186
225
  * import { isBigInt } from "effect/Predicate"
187
226
  *
188
- * assert.deepStrictEqual(isBigInt(1n), true)
227
+ * assert.strictEqual(isBigInt(1n), true)
189
228
  *
190
- * assert.deepStrictEqual(isBigInt(1), false)
229
+ * assert.strictEqual(isBigInt(1), false)
230
+ * assert.strictEqual(isBigInt("1"), false)
191
231
  * ```
192
232
  *
193
233
  * @category guards
@@ -195,16 +235,16 @@ export const isBoolean = input => typeof input === "boolean";
195
235
  */
196
236
  export const isBigInt = input => typeof input === "bigint";
197
237
  /**
198
- * Tests if a value is a `symbol`.
238
+ * A refinement that checks if a value is a `symbol`.
199
239
  *
200
240
  * @example
201
241
  * ```ts
202
242
  * import * as assert from "node:assert"
203
243
  * import { isSymbol } from "effect/Predicate"
204
244
  *
205
- * assert.deepStrictEqual(isSymbol(Symbol.for("a")), true)
245
+ * assert.strictEqual(isSymbol(Symbol.for("a")), true)
206
246
  *
207
- * assert.deepStrictEqual(isSymbol("a"), false)
247
+ * assert.strictEqual(isSymbol("a"), false)
208
248
  * ```
209
249
  *
210
250
  * @category guards
@@ -212,19 +252,23 @@ export const isBigInt = input => typeof input === "bigint";
212
252
  */
213
253
  export const isSymbol = input => typeof input === "symbol";
214
254
  // TODO: make public
215
- /** @internal */
255
+ /**
256
+ * A refinement that checks if a value is a valid `PropertyKey` (a `string`, `number`, or `symbol`).
257
+ * @internal
258
+ */
216
259
  export const isPropertyKey = u => isString(u) || isNumber(u) || isSymbol(u);
217
260
  /**
218
- * Tests if a value is a `function`.
261
+ * A refinement that checks if a value is a `Function`.
219
262
  *
220
263
  * @example
221
264
  * ```ts
222
265
  * import * as assert from "node:assert"
223
266
  * import { isFunction } from "effect/Predicate"
224
267
  *
225
- * assert.deepStrictEqual(isFunction(isFunction), true)
268
+ * assert.strictEqual(isFunction(() => {}), true)
269
+ * assert.strictEqual(isFunction(isFunction), true)
226
270
  *
227
- * assert.deepStrictEqual(isFunction("function"), false)
271
+ * assert.strictEqual(isFunction("function"), false)
228
272
  * ```
229
273
  *
230
274
  * @category guards
@@ -232,17 +276,17 @@ export const isPropertyKey = u => isString(u) || isNumber(u) || isSymbol(u);
232
276
  */
233
277
  export const isFunction = isFunction_;
234
278
  /**
235
- * Tests if a value is `undefined`.
279
+ * A refinement that checks if a value is `undefined`.
236
280
  *
237
281
  * @example
238
282
  * ```ts
239
283
  * import * as assert from "node:assert"
240
284
  * import { isUndefined } from "effect/Predicate"
241
285
  *
242
- * assert.deepStrictEqual(isUndefined(undefined), true)
286
+ * assert.strictEqual(isUndefined(undefined), true)
243
287
  *
244
- * assert.deepStrictEqual(isUndefined(null), false)
245
- * assert.deepStrictEqual(isUndefined("undefined"), false)
288
+ * assert.strictEqual(isUndefined(null), false)
289
+ * assert.strictEqual(isUndefined("undefined"), false)
246
290
  * ```
247
291
  *
248
292
  * @category guards
@@ -250,17 +294,17 @@ export const isFunction = isFunction_;
250
294
  */
251
295
  export const isUndefined = input => input === undefined;
252
296
  /**
253
- * Tests if a value is not `undefined`.
297
+ * A refinement that checks if a value is not `undefined`.
254
298
  *
255
299
  * @example
256
300
  * ```ts
257
301
  * import * as assert from "node:assert"
258
302
  * import { isNotUndefined } from "effect/Predicate"
259
303
  *
260
- * assert.deepStrictEqual(isNotUndefined(null), true)
261
- * assert.deepStrictEqual(isNotUndefined("undefined"), true)
304
+ * assert.strictEqual(isNotUndefined(null), true)
305
+ * assert.strictEqual(isNotUndefined("value"), true)
262
306
  *
263
- * assert.deepStrictEqual(isNotUndefined(undefined), false)
307
+ * assert.strictEqual(isNotUndefined(undefined), false)
264
308
  * ```
265
309
  *
266
310
  * @category guards
@@ -268,17 +312,17 @@ export const isUndefined = input => input === undefined;
268
312
  */
269
313
  export const isNotUndefined = input => input !== undefined;
270
314
  /**
271
- * Tests if a value is `null`.
315
+ * A refinement that checks if a value is `null`.
272
316
  *
273
317
  * @example
274
318
  * ```ts
275
319
  * import * as assert from "node:assert"
276
320
  * import { isNull } from "effect/Predicate"
277
321
  *
278
- * assert.deepStrictEqual(isNull(null), true)
322
+ * assert.strictEqual(isNull(null), true)
279
323
  *
280
- * assert.deepStrictEqual(isNull(undefined), false)
281
- * assert.deepStrictEqual(isNull("null"), false)
324
+ * assert.strictEqual(isNull(undefined), false)
325
+ * assert.strictEqual(isNull("null"), false)
282
326
  * ```
283
327
  *
284
328
  * @category guards
@@ -286,17 +330,17 @@ export const isNotUndefined = input => input !== undefined;
286
330
  */
287
331
  export const isNull = input => input === null;
288
332
  /**
289
- * Tests if a value is not `null`.
333
+ * A refinement that checks if a value is not `null`.
290
334
  *
291
335
  * @example
292
336
  * ```ts
293
337
  * import * as assert from "node:assert"
294
338
  * import { isNotNull } from "effect/Predicate"
295
339
  *
296
- * assert.deepStrictEqual(isNotNull(undefined), true)
297
- * assert.deepStrictEqual(isNotNull("null"), true)
340
+ * assert.strictEqual(isNotNull(undefined), true)
341
+ * assert.strictEqual(isNotNull("value"), true)
298
342
  *
299
- * assert.deepStrictEqual(isNotNull(null), false)
343
+ * assert.strictEqual(isNotNull(null), false)
300
344
  * ```
301
345
  *
302
346
  * @category guards
@@ -304,17 +348,16 @@ export const isNull = input => input === null;
304
348
  */
305
349
  export const isNotNull = input => input !== null;
306
350
  /**
307
- * A guard that always fails.
351
+ * A refinement that always returns `false`. The type is narrowed to `never`.
308
352
  *
309
353
  * @example
310
354
  * ```ts
311
355
  * import * as assert from "node:assert"
312
356
  * import { isNever } from "effect/Predicate"
313
357
  *
314
- * assert.deepStrictEqual(isNever(null), false)
315
- * assert.deepStrictEqual(isNever(undefined), false)
316
- * assert.deepStrictEqual(isNever({}), false)
317
- * assert.deepStrictEqual(isNever([]), false)
358
+ * assert.strictEqual(isNever(1), false)
359
+ * assert.strictEqual(isNever(null), false)
360
+ * assert.strictEqual(isNever({}), false)
318
361
  * ```
319
362
  *
320
363
  * @category guards
@@ -322,66 +365,96 @@ export const isNotNull = input => input !== null;
322
365
  */
323
366
  export const isNever = _ => false;
324
367
  /**
325
- * A guard that always succeeds.
368
+ * A refinement that always returns `true`. The type is narrowed to `unknown`.
326
369
  *
327
370
  * @example
328
371
  * ```ts
329
372
  * import * as assert from "node:assert"
330
373
  * import { isUnknown } from "effect/Predicate"
331
374
  *
332
- * assert.deepStrictEqual(isUnknown(null), true)
333
- * assert.deepStrictEqual(isUnknown(undefined), true)
334
- *
335
- * assert.deepStrictEqual(isUnknown({}), true)
336
- * assert.deepStrictEqual(isUnknown([]), true)
375
+ * assert.strictEqual(isUnknown(1), true)
376
+ * assert.strictEqual(isUnknown(null), true)
377
+ * assert.strictEqual(isUnknown({}), true)
337
378
  * ```
338
379
  *
339
380
  * @category guards
340
381
  * @since 2.0.0
341
382
  */
342
383
  export const isUnknown = _ => true;
343
- /** @internal */
384
+ /**
385
+ * Checks if the input is an object or an array.
386
+ * @internal
387
+ */
344
388
  export const isRecordOrArray = input => typeof input === "object" && input !== null;
345
389
  /**
346
- * Tests if a value is an `object`.
390
+ * A refinement that checks if a value is an `object`. Note that in JavaScript,
391
+ * arrays and functions are also considered objects.
347
392
  *
348
393
  * @example
349
394
  * ```ts
350
395
  * import * as assert from "node:assert"
351
396
  * import { isObject } from "effect/Predicate"
352
397
  *
353
- * assert.deepStrictEqual(isObject({}), true)
354
- * assert.deepStrictEqual(isObject([]), true)
398
+ * assert.strictEqual(isObject({}), true)
399
+ * assert.strictEqual(isObject([]), true)
400
+ * assert.strictEqual(isObject(() => {}), true)
355
401
  *
356
- * assert.deepStrictEqual(isObject(null), false)
357
- * assert.deepStrictEqual(isObject(undefined), false)
402
+ * assert.strictEqual(isObject(null), false)
403
+ * assert.strictEqual(isObject("hello"), false)
358
404
  * ```
359
405
  *
360
406
  * @category guards
361
407
  * @since 2.0.0
408
+ * @see isRecord to check for plain objects (excluding arrays and functions).
362
409
  */
363
410
  export const isObject = input => isRecordOrArray(input) || isFunction(input);
364
411
  /**
365
- * Checks whether a value is an `object` containing a specified property key.
412
+ * A refinement that checks if a value is an object-like value and has a specific property key.
413
+ *
414
+ * @example
415
+ * ```ts
416
+ * import * as assert from "node:assert"
417
+ * import { hasProperty } from "effect/Predicate"
418
+ *
419
+ * assert.strictEqual(hasProperty({ a: 1 }, "a"), true)
420
+ * assert.strictEqual(hasProperty({ a: 1 }, "b"), false)
421
+ *
422
+ * const value: unknown = { name: "Alice" };
423
+ * if (hasProperty(value, "name")) {
424
+ * // The type of `value` is narrowed to `{ name: unknown }`
425
+ * // and we can safely access `value.name`
426
+ * console.log(value.name)
427
+ * }
428
+ * ```
366
429
  *
367
430
  * @category guards
368
431
  * @since 2.0.0
369
432
  */
370
433
  export const hasProperty = /*#__PURE__*/dual(2, (self, property) => isObject(self) && property in self);
371
434
  /**
372
- * Tests if a value is an `object` with a property `_tag` that matches the given tag.
435
+ * A refinement that checks if a value is an object with a `_tag` property
436
+ * that matches the given tag. This is a powerful tool for working with
437
+ * discriminated union types.
373
438
  *
374
439
  * @example
375
440
  * ```ts
376
441
  * import * as assert from "node:assert"
377
442
  * import { isTagged } from "effect/Predicate"
378
443
  *
379
- * assert.deepStrictEqual(isTagged(1, "a"), false)
380
- * assert.deepStrictEqual(isTagged(null, "a"), false)
381
- * assert.deepStrictEqual(isTagged({}, "a"), false)
382
- * assert.deepStrictEqual(isTagged({ a: "a" }, "a"), false)
383
- * assert.deepStrictEqual(isTagged({ _tag: "a" }, "a"), true)
384
- * assert.deepStrictEqual(isTagged("a")({ _tag: "a" }), true)
444
+ * type Shape = { _tag: "circle"; radius: number } | { _tag: "square"; side: number }
445
+ *
446
+ * const isCircle = isTagged("circle")
447
+ *
448
+ * const shape1: Shape = { _tag: "circle", radius: 10 }
449
+ * const shape2: Shape = { _tag: "square", side: 5 }
450
+ *
451
+ * assert.strictEqual(isCircle(shape1), true)
452
+ * assert.strictEqual(isCircle(shape2), false)
453
+ *
454
+ * if (isCircle(shape1)) {
455
+ * // shape1 is now narrowed to { _tag: "circle"; radius: number }
456
+ * assert.strictEqual(shape1.radius, 10)
457
+ * }
385
458
  * ```
386
459
  *
387
460
  * @category guards
@@ -389,55 +462,59 @@ export const hasProperty = /*#__PURE__*/dual(2, (self, property) => isObject(sel
389
462
  */
390
463
  export const isTagged = /*#__PURE__*/dual(2, (self, tag) => hasProperty(self, "_tag") && self["_tag"] === tag);
391
464
  /**
392
- * A guard that succeeds when the input is `null` or `undefined`.
465
+ * A refinement that checks if a value is either `null` or `undefined`.
393
466
  *
394
467
  * @example
395
468
  * ```ts
396
469
  * import * as assert from "node:assert"
397
470
  * import { isNullable } from "effect/Predicate"
398
471
  *
399
- * assert.deepStrictEqual(isNullable(null), true)
400
- * assert.deepStrictEqual(isNullable(undefined), true)
472
+ * assert.strictEqual(isNullable(null), true)
473
+ * assert.strictEqual(isNullable(undefined), true)
401
474
  *
402
- * assert.deepStrictEqual(isNullable({}), false)
403
- * assert.deepStrictEqual(isNullable([]), false)
475
+ * assert.strictEqual(isNullable(0), false)
476
+ * assert.strictEqual(isNullable(""), false)
404
477
  * ```
405
478
  *
406
479
  * @category guards
407
480
  * @since 2.0.0
481
+ * @see isNotNullable
408
482
  */
409
483
  export const isNullable = input => input === null || input === undefined;
410
484
  /**
411
- * A guard that succeeds when the input is not `null` or `undefined`.
485
+ * A refinement that checks if a value is neither `null` nor `undefined`.
486
+ * The type is narrowed to `NonNullable<A>`.
412
487
  *
413
488
  * @example
414
489
  * ```ts
415
490
  * import * as assert from "node:assert"
416
491
  * import { isNotNullable } from "effect/Predicate"
417
492
  *
418
- * assert.deepStrictEqual(isNotNullable({}), true)
419
- * assert.deepStrictEqual(isNotNullable([]), true)
493
+ * assert.strictEqual(isNotNullable(0), true)
494
+ * assert.strictEqual(isNotNullable("hello"), true)
420
495
  *
421
- * assert.deepStrictEqual(isNotNullable(null), false)
422
- * assert.deepStrictEqual(isNotNullable(undefined), false)
496
+ * assert.strictEqual(isNotNullable(null), false)
497
+ * assert.strictEqual(isNotNullable(undefined), false)
423
498
  * ```
424
499
  *
425
500
  * @category guards
426
501
  * @since 2.0.0
502
+ * @see isNullable
427
503
  */
428
504
  export const isNotNullable = input => input !== null && input !== undefined;
429
505
  /**
430
- * A guard that succeeds when the input is an `Error`.
506
+ * A refinement that checks if a value is an instance of `Error`.
431
507
  *
432
508
  * @example
433
509
  * ```ts
434
510
  * import * as assert from "node:assert"
435
511
  * import { isError } from "effect/Predicate"
436
512
  *
437
- * assert.deepStrictEqual(isError(new Error()), true)
513
+ * assert.strictEqual(isError(new Error("boom")), true)
514
+ * assert.strictEqual(isError(new TypeError("boom")), true)
438
515
  *
439
- * assert.deepStrictEqual(isError(null), false)
440
- * assert.deepStrictEqual(isError({}), false)
516
+ * assert.strictEqual(isError({ message: "boom" }), false)
517
+ * assert.strictEqual(isError("boom"), false)
441
518
  * ```
442
519
  *
443
520
  * @category guards
@@ -445,17 +522,17 @@ export const isNotNullable = input => input !== null && input !== undefined;
445
522
  */
446
523
  export const isError = input => input instanceof Error;
447
524
  /**
448
- * A guard that succeeds when the input is a `Uint8Array`.
525
+ * A refinement that checks if a value is a `Uint8Array`.
449
526
  *
450
527
  * @example
451
528
  * ```ts
452
529
  * import * as assert from "node:assert"
453
530
  * import { isUint8Array } from "effect/Predicate"
454
531
  *
455
- * assert.deepStrictEqual(isUint8Array(new Uint8Array()), true)
532
+ * assert.strictEqual(isUint8Array(new Uint8Array()), true)
456
533
  *
457
- * assert.deepStrictEqual(isUint8Array(null), false)
458
- * assert.deepStrictEqual(isUint8Array({}), false)
534
+ * assert.strictEqual(isUint8Array(new Uint16Array()), false)
535
+ * assert.strictEqual(isUint8Array([1, 2, 3]), false)
459
536
  * ```
460
537
  *
461
538
  * @category guards
@@ -463,17 +540,17 @@ export const isError = input => input instanceof Error;
463
540
  */
464
541
  export const isUint8Array = input => input instanceof Uint8Array;
465
542
  /**
466
- * A guard that succeeds when the input is a `Date`.
543
+ * A refinement that checks if a value is a `Date` object.
467
544
  *
468
545
  * @example
469
546
  * ```ts
470
547
  * import * as assert from "node:assert"
471
548
  * import { isDate } from "effect/Predicate"
472
549
  *
473
- * assert.deepStrictEqual(isDate(new Date()), true)
550
+ * assert.strictEqual(isDate(new Date()), true)
474
551
  *
475
- * assert.deepStrictEqual(isDate(null), false)
476
- * assert.deepStrictEqual(isDate({}), false)
552
+ * assert.strictEqual(isDate(Date.now()), false) // `Date.now()` returns a number
553
+ * assert.strictEqual(isDate("2023-01-01"), false)
477
554
  * ```
478
555
  *
479
556
  * @category guards
@@ -481,18 +558,20 @@ export const isUint8Array = input => input instanceof Uint8Array;
481
558
  */
482
559
  export const isDate = input => input instanceof Date;
483
560
  /**
484
- * A guard that succeeds when the input is an `Iterable`.
561
+ * A refinement that checks if a value is an `Iterable`.
562
+ * Many built-in types are iterable, such as `Array`, `string`, `Map`, and `Set`.
485
563
  *
486
564
  * @example
487
565
  * ```ts
488
566
  * import * as assert from "node:assert"
489
567
  * import { isIterable } from "effect/Predicate"
490
568
  *
491
- * assert.deepStrictEqual(isIterable([]), true)
492
- * assert.deepStrictEqual(isIterable(new Set()), true)
569
+ * assert.strictEqual(isIterable([]), true)
570
+ * assert.strictEqual(isIterable("hello"), true)
571
+ * assert.strictEqual(isIterable(new Set()), true)
493
572
  *
494
- * assert.deepStrictEqual(isIterable(null), false)
495
- * assert.deepStrictEqual(isIterable({}), false)
573
+ * assert.strictEqual(isIterable({}), false)
574
+ * assert.strictEqual(isIterable(123), false)
496
575
  * ```
497
576
  *
498
577
  * @category guards
@@ -500,42 +579,44 @@ export const isDate = input => input instanceof Date;
500
579
  */
501
580
  export const isIterable = input => hasProperty(input, Symbol.iterator);
502
581
  /**
503
- * A guard that succeeds when the input is a record.
582
+ * A refinement that checks if a value is a record (i.e., a plain object).
583
+ * This check returns `false` for arrays, `null`, and functions.
504
584
  *
505
585
  * @example
506
586
  * ```ts
507
587
  * import * as assert from "node:assert"
508
588
  * import { isRecord } from "effect/Predicate"
509
589
  *
510
- * assert.deepStrictEqual(isRecord({}), true)
511
- * assert.deepStrictEqual(isRecord({ a: 1 }), true)
590
+ * assert.strictEqual(isRecord({}), true)
591
+ * assert.strictEqual(isRecord({ a: 1 }), true)
512
592
  *
513
- * assert.deepStrictEqual(isRecord([]), false)
514
- * assert.deepStrictEqual(isRecord([1, 2, 3]), false)
515
- * assert.deepStrictEqual(isRecord(null), false)
516
- * assert.deepStrictEqual(isRecord(undefined), false)
517
- * assert.deepStrictEqual(isRecord(() => null), false)
593
+ * assert.strictEqual(isRecord([]), false)
594
+ * assert.strictEqual(isRecord(new Date()), false)
595
+ * assert.strictEqual(isRecord(null), false)
596
+ * assert.strictEqual(isRecord(() => null), false)
518
597
  * ```
519
598
  *
520
599
  * @category guards
521
600
  * @since 2.0.0
601
+ * @see isObject
522
602
  */
523
603
  export const isRecord = input => isRecordOrArray(input) && !Array.isArray(input);
524
604
  /**
525
- * A guard that succeeds when the input is a readonly record.
605
+ * A refinement that checks if a value is a readonly record (i.e., a plain object).
606
+ * This check returns `false` for arrays, `null`, and functions.
607
+ *
608
+ * This is an alias for `isRecord`.
526
609
  *
527
610
  * @example
528
611
  * ```ts
529
612
  * import * as assert from "node:assert"
530
613
  * import { isReadonlyRecord } from "effect/Predicate"
531
614
  *
532
- * assert.deepStrictEqual(isReadonlyRecord({}), true)
533
- * assert.deepStrictEqual(isReadonlyRecord({ a: 1 }), true)
615
+ * assert.strictEqual(isReadonlyRecord({}), true)
616
+ * assert.strictEqual(isReadonlyRecord({ a: 1 }), true)
534
617
  *
535
- * assert.deepStrictEqual(isReadonlyRecord([]), false)
536
- * assert.deepStrictEqual(isReadonlyRecord([1, 2, 3]), false)
537
- * assert.deepStrictEqual(isReadonlyRecord(null), false)
538
- * assert.deepStrictEqual(isReadonlyRecord(undefined), false)
618
+ * assert.strictEqual(isReadonlyRecord([]), false)
619
+ * assert.strictEqual(isReadonlyRecord(null), false)
539
620
  * ```
540
621
  *
541
622
  * @category guards
@@ -543,36 +624,58 @@ export const isRecord = input => isRecordOrArray(input) && !Array.isArray(input)
543
624
  */
544
625
  export const isReadonlyRecord = isRecord;
545
626
  /**
546
- * A guard that succeeds when the input is a Promise.
627
+ * A refinement that checks if a value is a `Promise`. It performs a duck-typing check
628
+ * for `.then` and `.catch` methods.
547
629
  *
548
630
  * @example
549
631
  * ```ts
550
632
  * import * as assert from "node:assert"
551
633
  * import { isPromise } from "effect/Predicate"
552
634
  *
553
- * assert.deepStrictEqual(isPromise({}), false)
554
- * assert.deepStrictEqual(isPromise(Promise.resolve("hello")), true)
635
+ * assert.strictEqual(isPromise(Promise.resolve(1)), true)
636
+ * assert.strictEqual(isPromise(new Promise(() => {})), true)
637
+ *
638
+ * assert.strictEqual(isPromise({ then() {} }), false) // Missing .catch
639
+ * assert.strictEqual(isPromise({}), false)
555
640
  * ```
556
641
  *
557
642
  * @category guards
558
643
  * @since 2.0.0
644
+ * @see isPromiseLike
559
645
  */
560
646
  export const isPromise = input => hasProperty(input, "then") && "catch" in input && isFunction(input.then) && isFunction(input.catch);
561
647
  /**
648
+ * A refinement that checks if a value is `PromiseLike`. It performs a duck-typing
649
+ * check for a `.then` method.
650
+ *
651
+ * @example
652
+ * ```ts
653
+ * import * as assert from "node:assert"
654
+ * import { isPromiseLike } from "effect/Predicate"
655
+ *
656
+ * assert.strictEqual(isPromiseLike(Promise.resolve(1)), true)
657
+ * assert.strictEqual(isPromiseLike({ then: () => {} }), true)
658
+ *
659
+ * assert.strictEqual(isPromiseLike({}), false)
660
+ * ```
661
+ *
562
662
  * @category guards
563
663
  * @since 2.0.0
664
+ * @see isPromise
564
665
  */
565
666
  export const isPromiseLike = input => hasProperty(input, "then") && isFunction(input.then);
566
667
  /**
567
- * Tests if a value is a `RegExp`.
668
+ * A refinement that checks if a value is a `RegExp`.
568
669
  *
569
670
  * @example
570
671
  * ```ts
571
672
  * import * as assert from "node:assert"
572
673
  * import { Predicate } from "effect"
573
674
  *
574
- * assert.deepStrictEqual(Predicate.isRegExp(/a/), true)
575
- * assert.deepStrictEqual(Predicate.isRegExp("a"), false)
675
+ * assert.strictEqual(Predicate.isRegExp(/a/), true)
676
+ * assert.strictEqual(Predicate.isRegExp(new RegExp("a")), true)
677
+ *
678
+ * assert.strictEqual(Predicate.isRegExp("/a/"), false)
576
679
  * ```
577
680
  *
578
681
  * @category guards
@@ -580,17 +683,53 @@ export const isPromiseLike = input => hasProperty(input, "then") && isFunction(i
580
683
  */
581
684
  export const isRegExp = input => input instanceof RegExp;
582
685
  /**
686
+ * Composes a `Refinement` with another `Refinement` or `Predicate`.
687
+ *
688
+ * This can be used to chain checks. The first refinement is applied, and if it
689
+ * passes, the second check is applied to the same value, potentially refining
690
+ * the type further.
691
+ *
692
+ * @example
693
+ * ```ts
694
+ * import { Predicate } from "effect"
695
+ * import * as assert from "node:assert"
696
+ *
697
+ * const isString = (u: unknown): u is string => typeof u === "string"
698
+ * const minLength = (n: number) => (s: string): boolean => s.length >= n
699
+ *
700
+ * // Create a refinement that checks for a string with a minimum length of 3
701
+ * const isLongString = Predicate.compose(isString, minLength(3))
702
+ *
703
+ * let value: unknown = "hello"
704
+ *
705
+ * assert.strictEqual(isLongString(value), true)
706
+ * if (isLongString(value)) {
707
+ * // value is narrowed to string
708
+ * assert.strictEqual(value.toUpperCase(), "HELLO")
709
+ * }
710
+ * assert.strictEqual(isLongString("hi"), false)
711
+ * ```
712
+ *
583
713
  * @since 2.0.0
584
714
  */
585
715
  export const compose = /*#__PURE__*/dual(2, (ab, bc) => a => ab(a) && bc(a));
586
716
  /**
717
+ * Combines two predicates to test a tuple of two values. The first predicate tests the
718
+ * first element of the tuple, and the second predicate tests the second element.
719
+ *
587
720
  * @category combining
588
721
  * @since 2.0.0
589
722
  */
590
723
  export const product = (self, that) => ([a, b]) => self(a) && that(b);
591
724
  /**
725
+ * Takes an iterable of predicates and returns a new predicate that tests an array of values.
726
+ * The new predicate returns `true` if each predicate at a given index is satisfied by the
727
+ * value at the same index in the array. The check stops at the length of the shorter of
728
+ * the two iterables (predicates or values).
729
+ *
592
730
  * @category combining
593
731
  * @since 2.0.0
732
+ * @see tuple for a more powerful, variadic version.
594
733
  */
595
734
  export const all = collection => {
596
735
  return as => {
@@ -608,6 +747,9 @@ export const all = collection => {
608
747
  };
609
748
  };
610
749
  /**
750
+ * Combines a predicate for a single value and an iterable of predicates for the rest of an array.
751
+ * Useful for checking the head and tail of an array separately.
752
+ *
611
753
  * @category combining
612
754
  * @since 2.0.0
613
755
  */
@@ -616,22 +758,67 @@ export const productMany = (self, collection) => {
616
758
  return ([head, ...tail]) => self(head) === false ? false : rest(tail);
617
759
  };
618
760
  /**
619
- * Similar to `Promise.all` but operates on `Predicate`s.
761
+ * Combines an array of predicates into a single predicate that tests an array of values.
762
+ * This function is highly type-aware and will produce a `Refinement` if any of the provided
763
+ * predicates are `Refinement`s, allowing for powerful type-narrowing of tuples.
764
+ *
765
+ * - If all predicates are `Predicate<T>`, the result is `Predicate<[T, T, ...]>`.
766
+ * - If any predicate is a `Refinement<A, B>`, the result is a `Refinement` that narrows
767
+ * the input tuple type to a more specific tuple type.
620
768
  *
621
- * ```ts skip-type-checking
622
- * [Refinement<A, B>, Refinement<C, D>, ...] -> Refinement<[A, C, ...], [B, D, ...]>
623
- * [Predicate<A>, Predicate<B>, ...] -> Predicate<[A, B, ...]>
624
- * [Refinement<A, B>, Predicate<C>, ...] -> Refinement<[A, C, ...], [B, C, ...]>
769
+ * @example
770
+ * ```ts
771
+ * import * as assert from "node:assert"
772
+ * import { Predicate } from "effect"
773
+ *
774
+ * const isString = (u: unknown): u is string => typeof u === "string"
775
+ * const isNumber = (u: unknown): u is number => typeof u === "number"
776
+ *
777
+ * // Create a refinement for a [string, number] tuple
778
+ * const isStringNumberTuple = Predicate.tuple(isString, isNumber)
779
+ *
780
+ * const value: [unknown, unknown] = ["hello", 123]
781
+ * if (isStringNumberTuple(value)) {
782
+ * // value is narrowed to [string, number]
783
+ * const [s, n] = value
784
+ * assert.strictEqual(s.toUpperCase(), "HELLO")
785
+ * assert.strictEqual(n.toFixed(2), "123.00")
786
+ * }
787
+ * assert.strictEqual(isStringNumberTuple(["hello", "123"]), false)
625
788
  * ```
626
789
  *
627
790
  * @since 2.0.0
628
791
  */
629
792
  export const tuple = (...elements) => all(elements);
630
793
  /**
631
- * ```ts skip-type-checking
632
- * { ab: Refinement<A, B>; cd: Refinement<C, D>, ... } -> Refinement<{ ab: A; cd: C; ... }, { ab: B; cd: D; ... }>
633
- * { a: Predicate<A, B>; b: Predicate<B>, ... } -> Predicate<{ a: A; b: B; ... }>
634
- * { ab: Refinement<A, B>; c: Predicate<C>, ... } -> Refinement<{ ab: A; c: C; ... }, { ab: B; c: С; ... }>
794
+ * Combines a record of predicates into a single predicate that tests a record of values.
795
+ * This function is highly type-aware and will produce a `Refinement` if any of the provided
796
+ * predicates are `Refinement`s, allowing for powerful type-narrowing of structs.
797
+ *
798
+ * - If all predicates are `Predicate<T>`, the result is `Predicate<{ k: T, ... }>`.
799
+ * - If any predicate is a `Refinement<A, B>`, the result is a `Refinement` that narrows
800
+ * the input record type to a more specific record type.
801
+ *
802
+ * @example
803
+ * ```ts
804
+ * import * as assert from "node:assert"
805
+ * import { Predicate } from "effect"
806
+ *
807
+ * const isString = (u: unknown): u is string => typeof u === "string"
808
+ * const isNumber = (u: unknown): u is number => typeof u === "number"
809
+ *
810
+ * const personPredicate = Predicate.struct({
811
+ * name: isString,
812
+ * age: isNumber
813
+ * })
814
+ *
815
+ * const value: { name: unknown; age: unknown } = { name: "Alice", age: 30 }
816
+ * if (personPredicate(value)) {
817
+ * // value is narrowed to { name: string; age: number }
818
+ * assert.strictEqual(value.name.toUpperCase(), "ALICE")
819
+ * assert.strictEqual(value.age.toFixed(0), "30")
820
+ * }
821
+ * assert.strictEqual(personPredicate({ name: "Bob", age: "40" }), false)
635
822
  * ```
636
823
  *
637
824
  * @since 2.0.0
@@ -648,18 +835,21 @@ export const struct = fields => {
648
835
  };
649
836
  };
650
837
  /**
651
- * Negates the result of a given predicate.
838
+ * Returns a new predicate that is the logical negation of the given predicate.
839
+ *
840
+ * **Note**: If the input is a `Refinement`, the resulting predicate will be a
841
+ * simple `Predicate`, as TypeScript cannot infer the negative type.
652
842
  *
653
843
  * @example
654
844
  * ```ts
655
845
  * import * as assert from "node:assert"
656
846
  * import { Predicate, Number } from "effect"
657
847
  *
658
- * const isPositive = Predicate.not(Number.lessThan(0))
848
+ * const isNonPositive = Predicate.not(Number.greaterThan(0))
659
849
  *
660
- * assert.deepStrictEqual(isPositive(-1), false)
661
- * assert.deepStrictEqual(isPositive(0), true)
662
- * assert.deepStrictEqual(isPositive(1), true)
850
+ * assert.strictEqual(isNonPositive(-1), true)
851
+ * assert.strictEqual(isNonPositive(0), true)
852
+ * assert.strictEqual(isNonPositive(1), false)
663
853
  * ```
664
854
  *
665
855
  * @category combinators
@@ -667,18 +857,31 @@ export const struct = fields => {
667
857
  */
668
858
  export const not = self => a => !self(a);
669
859
  /**
670
- * Combines two predicates into a new predicate that returns `true` if at least one of the predicates returns `true`.
860
+ * Combines two predicates with a logical "OR". The resulting predicate returns `true`
861
+ * if at least one of the predicates returns `true`.
862
+ *
863
+ * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the
864
+ * union of their target types (`B | C`).
671
865
  *
672
866
  * @example
673
867
  * ```ts
674
868
  * import * as assert from "node:assert"
675
- * import { Predicate, Number } from "effect"
869
+ * import { Predicate } from "effect"
870
+ *
871
+ * const isString = (u: unknown): u is string => typeof u === "string"
872
+ * const isNumber = (u: unknown): u is number => typeof u === "number"
873
+ *
874
+ * const isStringOrNumber = Predicate.or(isString, isNumber)
676
875
  *
677
- * const nonZero = Predicate.or(Number.lessThan(0), Number.greaterThan(0))
876
+ * assert.strictEqual(isStringOrNumber("hello"), true)
877
+ * assert.strictEqual(isStringOrNumber(123), true)
878
+ * assert.strictEqual(isStringOrNumber(null), false)
678
879
  *
679
- * assert.deepStrictEqual(nonZero(-1), true)
680
- * assert.deepStrictEqual(nonZero(0), false)
681
- * assert.deepStrictEqual(nonZero(1), true)
880
+ * const value: unknown = "world"
881
+ * if (isStringOrNumber(value)) {
882
+ * // value is narrowed to string | number
883
+ * console.log(value)
884
+ * }
682
885
  * ```
683
886
  *
684
887
  * @category combinators
@@ -686,21 +889,34 @@ export const not = self => a => !self(a);
686
889
  */
687
890
  export const or = /*#__PURE__*/dual(2, (self, that) => a => self(a) || that(a));
688
891
  /**
689
- * Combines two predicates into a new predicate that returns `true` if both of the predicates returns `true`.
892
+ * Combines two predicates with a logical "AND". The resulting predicate returns `true`
893
+ * only if both of the predicates return `true`.
894
+ *
895
+ * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the
896
+ * intersection of their target types (`B & C`).
690
897
  *
691
898
  * @example
692
899
  * ```ts
693
900
  * import * as assert from "node:assert"
694
901
  * import { Predicate } from "effect"
695
902
  *
696
- * const minLength = (n: number) => (s: string) => s.length >= n
697
- * const maxLength = (n: number) => (s: string) => s.length <= n
903
+ * type Person = { name: string }
904
+ * type Employee = { id: number }
698
905
  *
699
- * const length = (n: number) => Predicate.and(minLength(n), maxLength(n))
906
+ * const hasName = (u: unknown): u is Person => Predicate.hasProperty(u, "name") && typeof (u as any).name === "string"
907
+ * const hasId = (u: unknown): u is Employee => Predicate.hasProperty(u, "id") && typeof (u as any).id === "number"
700
908
  *
701
- * assert.deepStrictEqual(length(2)("aa"), true)
702
- * assert.deepStrictEqual(length(2)("a"), false)
703
- * assert.deepStrictEqual(length(2)("aaa"), false)
909
+ * const isPersonAndEmployee = Predicate.and(hasName, hasId)
910
+ *
911
+ * const val: unknown = { name: "Alice", id: 123 }
912
+ * if (isPersonAndEmployee(val)) {
913
+ * // val is narrowed to Person & Employee
914
+ * assert.strictEqual(val.name, "Alice")
915
+ * assert.strictEqual(val.id, 123)
916
+ * }
917
+ *
918
+ * assert.strictEqual(isPersonAndEmployee({ name: "Bob" }), false) // Missing id
919
+ * assert.strictEqual(isPersonAndEmployee({ id: 456 }), false) // Missing name
704
920
  * ```
705
921
  *
706
922
  * @category combinators
@@ -708,57 +924,107 @@ export const or = /*#__PURE__*/dual(2, (self, that) => a => self(a) || that(a));
708
924
  */
709
925
  export const and = /*#__PURE__*/dual(2, (self, that) => a => self(a) && that(a));
710
926
  /**
927
+ * Combines two predicates with a logical "XOR" (exclusive OR). The resulting predicate
928
+ * returns `true` if one of the predicates returns `true`, but not both.
929
+ *
930
+ * @example
931
+ * ```ts
932
+ * import * as assert from "node:assert"
933
+ * import { Predicate } from "effect"
934
+ *
935
+ * const isPositive = (n: number) => n > 0
936
+ * const isEven = (n: number) => n % 2 === 0
937
+ *
938
+ * const isPositiveXorEven = Predicate.xor(isPositive, isEven)
939
+ *
940
+ * assert.strictEqual(isPositiveXorEven(4), false) // both true -> false
941
+ * assert.strictEqual(isPositiveXorEven(3), true) // one true -> true
942
+ * assert.strictEqual(isPositiveXorEven(-2), true) // one true -> true
943
+ * assert.strictEqual(isPositiveXorEven(-1), false) // both false -> false
944
+ * ```
945
+ *
711
946
  * @category combinators
712
947
  * @since 2.0.0
713
948
  */
714
949
  export const xor = /*#__PURE__*/dual(2, (self, that) => a => self(a) !== that(a));
715
950
  /**
951
+ * Combines two predicates with a logical "EQV" (equivalence). The resulting predicate
952
+ * returns `true` if both predicates return the same boolean value (both `true` or both `false`).
953
+ *
954
+ * @example
955
+ * ```ts
956
+ * import * as assert from "node:assert"
957
+ * import { Predicate } from "effect"
958
+ *
959
+ * const isPositive = (n: number) => n > 0
960
+ * const isEven = (n: number) => n % 2 === 0
961
+ *
962
+ * const isPositiveEqvEven = Predicate.eqv(isPositive, isEven)
963
+ *
964
+ * assert.strictEqual(isPositiveEqvEven(4), true) // both true -> true
965
+ * assert.strictEqual(isPositiveEqvEven(3), false) // different -> false
966
+ * assert.strictEqual(isPositiveEqvEven(-2), false) // different -> false
967
+ * assert.strictEqual(isPositiveEqvEven(-1), true) // both false -> true
968
+ * ```
969
+ *
716
970
  * @category combinators
717
971
  * @since 2.0.0
718
972
  */
719
973
  export const eqv = /*#__PURE__*/dual(2, (self, that) => a => self(a) === that(a));
720
974
  /**
721
- * Represents the logical implication combinator for predicates. In formal
722
- * logic, the implication operator `->` denotes that if the first proposition
723
- * (antecedent) is true, then the second proposition (consequent) must also be
724
- * true. In simpler terms, `p implies q` can be interpreted as "if p then q". If
725
- * the first predicate holds, then the second predicate must hold
726
- * for the given context.
975
+ * Creates a predicate that represents a logical "if-then" rule.
976
+ *
977
+ * Think of it as a conditional promise: **"If `antecedent` holds true, then I promise `consequent` will also be true."**
727
978
  *
728
- * In practical terms within TypeScript, `p implies q` is equivalent to `!p || (p && q)`.
979
+ * This function is invaluable for defining complex validation logic where one condition dictates another.
729
980
  *
730
- * Note that if the antecedent is `false`, the result is `true` by default
731
- * because the outcome of the consequent cannot be determined.
981
+ * ### How It Works
732
982
  *
733
- * This function is useful in situations where you need to enforce rules or
734
- * constraints that are contingent on certain conditions.
735
- * It proves especially helpful in defining property tests.
983
+ * The rule only fails (returns `false`) when the "if" part is `true`, but the "then" part is `false`.
984
+ * In all other cases, the promise is considered kept, and the result is `true`.
736
985
  *
737
- * The example below illustrates the transitive property of order using the
738
- * `implies` function. In simple terms, if `a <= b` and `b <= c`, then `a <= c`
739
- * must be true.
986
+ * This includes the concept of **"vacuous truth"**: if the "if" part is `false`, the rule doesn't apply,
987
+ * so the promise isn't broken, and the result is `true`. (e.g., "If it rains, I'll bring an umbrella."
988
+ * If it doesn't rain, you haven't broken your promise, no matter what).
989
+ *
990
+ * ### Key Details
991
+ *
992
+ * - **Logical Equivalence**: `implies(p, q)` is the same as `not(p).or(q)`, or simply `!p || q`
993
+ * in plain JavaScript. This can be a helpful way to reason about its behavior.
994
+ *
995
+ * - **Type-Safety Warning**: This function always returns a `Predicate`, never a type-narrowing
996
+ * `Refinement`. A `true` result doesn't guarantee the `consequent` passed (it could be `true`
997
+ * simply because the `antecedent` was `false`), so it cannot be used to safely narrow a type.
740
998
  *
741
999
  * @example
742
1000
  * ```ts
1001
+ * // Rule: A user can only be an admin if they also belong to the "staff" group.
743
1002
  * import * as assert from "node:assert"
744
1003
  * import { Predicate } from "effect"
745
1004
  *
746
- * type Triple = {
747
- * readonly a: number
748
- * readonly b: number
749
- * readonly c: number
1005
+ * type User = {
1006
+ * isStaff: boolean
1007
+ * isAdmin: boolean
750
1008
  * }
751
1009
  *
752
- * const transitivity = Predicate.implies(
753
- * // antecedent
754
- * (input: Triple) => input.a <= input.b && input.b <= input.c,
755
- * // consequent
756
- * (input: Triple) => input.a <= input.c
1010
+ * const isValidUserPermission = Predicate.implies(
1011
+ * // antecedent: "if" the user is an admin...
1012
+ * (user: User) => user.isAdmin,
1013
+ * // consequent: "then" they must be staff.
1014
+ * (user: User) => user.isStaff
757
1015
  * )
758
1016
  *
759
- * assert.equal(transitivity({ a: 1, b: 2, c: 3 }), true)
760
- * // antecedent is `false`, so the result is `true`
761
- * assert.equal(transitivity({ a: 1, b: 0, c: 0 }), true)
1017
+ * // A non-admin who is not staff. Rule doesn't apply (antecedent is false).
1018
+ * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: false }), true)
1019
+ *
1020
+ * // A staff member who is not an admin. Rule doesn't apply (antecedent is false).
1021
+ * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: false }), true)
1022
+ *
1023
+ * // An admin who is also staff. The rule was followed.
1024
+ * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: true }), true)
1025
+ *
1026
+ * // An admin who is NOT staff. The rule was broken!
1027
+ * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: true }), false)
762
1028
  * ```
763
1029
  *
764
1030
  * @category combinators
@@ -766,18 +1032,47 @@ export const eqv = /*#__PURE__*/dual(2, (self, that) => a => self(a) === that(a)
766
1032
  */
767
1033
  export const implies = /*#__PURE__*/dual(2, (antecedent, consequent) => a => antecedent(a) ? consequent(a) : true);
768
1034
  /**
1035
+ * Combines two predicates with a logical "NOR" (negated OR). The resulting predicate
1036
+ * returns `true` only if both predicates return `false`.
1037
+ * This is equivalent to `not(or(p, q))`.
1038
+ *
769
1039
  * @category combinators
770
1040
  * @since 2.0.0
771
1041
  */
772
1042
  export const nor = /*#__PURE__*/dual(2, (self, that) => a => !(self(a) || that(a)));
773
1043
  /**
1044
+ * Combines two predicates with a logical "NAND" (negated AND). The resulting predicate
1045
+ * returns `true` if at least one of the predicates returns `false`.
1046
+ * This is equivalent to `not(and(p, q))`.
1047
+ *
774
1048
  * @category combinators
775
1049
  * @since 2.0.0
776
1050
  */
777
1051
  export const nand = /*#__PURE__*/dual(2, (self, that) => a => !(self(a) && that(a)));
778
1052
  /**
1053
+ * Takes an iterable of predicates and returns a new predicate. The new predicate
1054
+ * returns `true` if all predicates in the collection return `true` for a given value.
1055
+ *
1056
+ * This is like `Array.prototype.every` but for a collection of predicates.
1057
+ *
1058
+ * @example
1059
+ * ```ts
1060
+ * import * as assert from "node:assert"
1061
+ * import { Predicate } from "effect"
1062
+ *
1063
+ * const isPositive = (n: number) => n > 0
1064
+ * const isEven = (n: number) => n % 2 === 0
1065
+ *
1066
+ * const isPositiveAndEven = Predicate.every([isPositive, isEven])
1067
+ *
1068
+ * assert.strictEqual(isPositiveAndEven(4), true)
1069
+ * assert.strictEqual(isPositiveAndEven(3), false)
1070
+ * assert.strictEqual(isPositiveAndEven(-2), false)
1071
+ * ```
1072
+ *
779
1073
  * @category elements
780
1074
  * @since 2.0.0
1075
+ * @see some
781
1076
  */
782
1077
  export const every = collection => a => {
783
1078
  for (const p of collection) {
@@ -788,8 +1083,29 @@ export const every = collection => a => {
788
1083
  return true;
789
1084
  };
790
1085
  /**
1086
+ * Takes an iterable of predicates and returns a new predicate. The new predicate
1087
+ * returns `true` if at least one predicate in the collection returns `true` for a given value.
1088
+ *
1089
+ * This is like `Array.prototype.some` but for a collection of predicates.
1090
+ *
1091
+ * @example
1092
+ * ```ts
1093
+ * import * as assert from "node:assert"
1094
+ * import { Predicate } from "effect"
1095
+ *
1096
+ * const isNegative = (n: number) => n < 0
1097
+ * const isOdd = (n: number) => n % 2 !== 0
1098
+ *
1099
+ * const isNegativeOrOdd = Predicate.some([isNegative, isOdd])
1100
+ *
1101
+ * assert.strictEqual(isNegativeOrOdd(-2), true) // isNegative is true
1102
+ * assert.strictEqual(isNegativeOrOdd(3), true) // isOdd is true
1103
+ * assert.strictEqual(isNegativeOrOdd(4), false) // both are false
1104
+ * ```
1105
+ *
791
1106
  * @category elements
792
1107
  * @since 2.0.0
1108
+ * @see every
793
1109
  */
794
1110
  export const some = collection => a => {
795
1111
  for (const p of collection) {