From 89b2f65e4d25969b64001d0cb0164eea486922bb Mon Sep 17 00:00:00 2001 From: Laure Retru-Chavastel Date: Tue, 4 Jun 2024 19:12:14 +0200 Subject: [PATCH 1/8] Add changeset --- .changeset/two-bananas-mix.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/two-bananas-mix.md diff --git a/.changeset/two-bananas-mix.md b/.changeset/two-bananas-mix.md new file mode 100644 index 0000000000..e569ba4aba --- /dev/null +++ b/.changeset/two-bananas-mix.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Add Either.liftPredicate From 6a948611910cd1c349f72959e8089bbaa91e4dde Mon Sep 17 00:00:00 2001 From: Laure Retru-Chavastel Date: Tue, 4 Jun 2024 19:16:52 +0200 Subject: [PATCH 2/8] Add Either.liftPredicate --- packages/effect/src/Either.ts | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/packages/effect/src/Either.ts b/packages/effect/src/Either.ts index 4f1566d658..851cf0ea99 100644 --- a/packages/effect/src/Either.ts +++ b/packages/effect/src/Either.ts @@ -390,6 +390,57 @@ export const match: { }): B | C => isLeft(self) ? onLeft(self.left) : onRight(self.right) ) +/** + * Transforms a `Predicate` function into a `Right` of the input value if the predicate returns `true` + * or `Left` of the result of the provided function if the predicate returns false + * + * @param predicate - A `Predicate` function that takes in a value of type `A` and returns a boolean. + * + * @example + * import { pipe, Either } from "effect" + * + * const isPositive = (n: number): boolean => n > 0 + * + * assert.deepStrictEqual( + * pipe( + * 1, + * Either.liftPredicate(isPositive, n => `${n} is not positive`) + * ), + * Either.right(1) + * ) + * assert.deepStrictEqual( + * pipe( + * 0, + * Either.liftPredicate(isPositive, n => `${n} is not positive`) + * ), + * Either.left("0 is not positive") + * ) + * + * @category lifting + * @since 2.0.0 + */ +export const liftPredicate: { + (refinement: Refinement, B>, orLeftWith: (a: NoInfer) => E): (a: A) => Either + ( + predicate: Predicate>, + orLeftWith: (a: NoInfer) => E + ): (x: A) => Either + ( + self: A, + refinement: Refinement, + orLeftWith: (a: A) => E + ): Either + ( + self: A, + predicate: Predicate, + orLeftWith: (a: A) => E + ): Either +} = dual( + 3, + (x: B, predicate: Predicate, orLeftWith: (a: A) => E) => + predicate(x) ? right(x) : left(orLeftWith(x)) +) + /** * Filter the right value with the provided function. * If the predicate fails, set the left value with the result of the provided function. From bbd063acbc4b4a0369f9eee1160a7abfb85a83a2 Mon Sep 17 00:00:00 2001 From: Laure Retru-Chavastel Date: Tue, 4 Jun 2024 19:27:15 +0200 Subject: [PATCH 3/8] Add tests --- packages/effect/dtslint/Either.ts | 74 +++++++++++++++++++++++++++++ packages/effect/test/Either.test.ts | 41 ++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/packages/effect/dtslint/Either.ts b/packages/effect/dtslint/Either.ts index 30609ee059..f2d240b442 100644 --- a/packages/effect/dtslint/Either.ts +++ b/packages/effect/dtslint/Either.ts @@ -110,6 +110,80 @@ string$string.pipe(Either.andThen(string$number)) // $ExpectType Either string$string.pipe(Either.andThen(() => string$number)) +// ------------------------------------------------------------------------------------- +// liftPredicate +// ------------------------------------------------------------------------------------- + +declare const primitiveNumber: number +declare const primitiveNumberOrString: string | number +declare const predicateNumbersOrStrings: Predicate.Predicate + +// $ExpectType Either +pipe( + primitiveNumberOrString, + Either.liftPredicate(Predicate.isString, ( + _s // $ExpectType string | number + ) => "b" as const) +) + +// $ExpectType Either +Either.liftPredicate(primitiveNumberOrString, Predicate.isString, ( + _s // $ExpectType string | number +) => "b" as const) + +// $ExpectType Either +pipe( + primitiveNumberOrString, + Either.liftPredicate( + ( + n // $ExpectType string | number + ): n is number => typeof n === "number", + ( + _s // $ExpectType string | number + ) => "b" as const + ) +) + +// $ExpectType Either +Either.liftPredicate( + primitiveNumberOrString, + ( + n // $ExpectType string | number + ): n is number => typeof n === "number", + ( + _s // $ExpectType string | number + ) => "b" as const +) + +// $ExpectType Either +pipe( + primitiveNumberOrString, + Either.liftPredicate(predicateNumbersOrStrings, ( + _s // $ExpectType string | number + ) => "b" as const) +) + +// $ExpectType Either +pipe( + primitiveNumber, + Either.liftPredicate(predicateNumbersOrStrings, ( + _s // $ExpectType number + ) => "b" as const) +) + +// $ExpectType Either +pipe( + primitiveNumber, + Either.liftPredicate( + ( + _n // $ExpectType number + ) => true, + ( + _s // $ExpectType number + ) => "b" as const + ) +) + // ------------------------------------------------------------------------------------- // filterOrLeft // ------------------------------------------------------------------------------------- diff --git a/packages/effect/test/Either.test.ts b/packages/effect/test/Either.test.ts index 889775339d..37174bbc6f 100644 --- a/packages/effect/test/Either.test.ts +++ b/packages/effect/test/Either.test.ts @@ -169,6 +169,47 @@ describe("Either", () => { Util.deepStrictEqual(Either.flip(Either.left("b")), Either.right("b")) }) + it("liftPredicate", () => { + const isPositivePredicate = (n: number) => n > 0 + const onPositivePredicateError = (n: number) => `${n} is not positive` + const isNumberRefinement = (n: string | number): n is number => typeof n === "number" + const onNumberRefinementError = (n: string | number) => `${n} is not a number` + + Util.deepStrictEqual( + pipe(1, Either.liftPredicate(isPositivePredicate, onPositivePredicateError)), + Either.right(1) + ) + Util.deepStrictEqual( + pipe(-1, Either.liftPredicate(isPositivePredicate, onPositivePredicateError)), + Either.left(`-1 is not positive`) + ) + Util.deepStrictEqual( + pipe(1, Either.liftPredicate(isNumberRefinement, onNumberRefinementError)), + Either.right(1) + ) + Util.deepStrictEqual( + pipe("string", Either.liftPredicate(isNumberRefinement, onNumberRefinementError)), + Either.left(`string is not a number`) + ) + + Util.deepStrictEqual( + Either.liftPredicate(1, isPositivePredicate, onPositivePredicateError), + Either.right(1) + ) + Util.deepStrictEqual( + Either.liftPredicate(-1, isPositivePredicate, onPositivePredicateError), + Either.left(`-1 is not positive`) + ) + Util.deepStrictEqual( + Either.liftPredicate(1, isNumberRefinement, onNumberRefinementError), + Either.right(1) + ) + Util.deepStrictEqual( + Either.liftPredicate("string", isNumberRefinement, onNumberRefinementError), + Either.left(`string is not a number`) + ) + }) + it("filterOrLeft", () => { Util.deepStrictEqual(Either.filterOrLeft(Either.right(1), (n) => n > 0, () => "a"), Either.right(1)) Util.deepStrictEqual(Either.filterOrLeft(Either.right(1), (n) => n > 1, () => "a"), Either.left("a")) From 8c84ce127814f3165957acf32b5cf05317e8be3d Mon Sep 17 00:00:00 2001 From: Laure Retru-Chavastel Date: Tue, 4 Jun 2024 19:36:37 +0200 Subject: [PATCH 4/8] Update since value --- packages/effect/src/Either.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/effect/src/Either.ts b/packages/effect/src/Either.ts index 851cf0ea99..b43872f09a 100644 --- a/packages/effect/src/Either.ts +++ b/packages/effect/src/Either.ts @@ -417,7 +417,7 @@ export const match: { * ) * * @category lifting - * @since 2.0.0 + * @since 3.2.8 */ export const liftPredicate: { (refinement: Refinement, B>, orLeftWith: (a: NoInfer) => E): (a: A) => Either From 82efbe5784087b09a3bf1bd096fe97f916b565e6 Mon Sep 17 00:00:00 2001 From: Laure Retru-Chavastel Date: Thu, 6 Jun 2024 13:10:17 +0200 Subject: [PATCH 5/8] Remove redundant type parameter and adjust variable names --- packages/effect/src/Either.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/effect/src/Either.ts b/packages/effect/src/Either.ts index b43872f09a..2ca2a7ed4f 100644 --- a/packages/effect/src/Either.ts +++ b/packages/effect/src/Either.ts @@ -421,24 +421,24 @@ export const match: { */ export const liftPredicate: { (refinement: Refinement, B>, orLeftWith: (a: NoInfer) => E): (a: A) => Either - ( + ( predicate: Predicate>, orLeftWith: (a: NoInfer) => E - ): (x: A) => Either + ): (a: A) => Either ( self: A, refinement: Refinement, orLeftWith: (a: A) => E ): Either - ( + ( self: A, predicate: Predicate, orLeftWith: (a: A) => E - ): Either + ): Either } = dual( 3, - (x: B, predicate: Predicate, orLeftWith: (a: A) => E) => - predicate(x) ? right(x) : left(orLeftWith(x)) + (a: A, predicate: Predicate, orLeftWith: (a: A) => E): Either => + predicate(a) ? right(a) : left(orLeftWith(a)) ) /** From c9dcbafaeea882568d39bca871b00c14fce02e18 Mon Sep 17 00:00:00 2001 From: Laure Retru-Chavastel Date: Thu, 6 Jun 2024 13:35:57 +0200 Subject: [PATCH 6/8] Fix typing issue in test --- packages/effect/src/Either.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/effect/src/Either.ts b/packages/effect/src/Either.ts index 2ca2a7ed4f..6143e8f943 100644 --- a/packages/effect/src/Either.ts +++ b/packages/effect/src/Either.ts @@ -432,8 +432,8 @@ export const liftPredicate: { ): Either ( self: A, - predicate: Predicate, - orLeftWith: (a: A) => E + predicate: Predicate>, + orLeftWith: (a: NoInfer) => E ): Either } = dual( 3, From 72868f977980a51000d74d6a8459258f204409e3 Mon Sep 17 00:00:00 2001 From: Laure Retru-Chavastel Date: Thu, 6 Jun 2024 13:36:38 +0200 Subject: [PATCH 7/8] Update since value --- packages/effect/src/Either.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/effect/src/Either.ts b/packages/effect/src/Either.ts index 6143e8f943..51a62f63df 100644 --- a/packages/effect/src/Either.ts +++ b/packages/effect/src/Either.ts @@ -417,7 +417,7 @@ export const match: { * ) * * @category lifting - * @since 3.2.8 + * @since 3.4.0 */ export const liftPredicate: { (refinement: Refinement, B>, orLeftWith: (a: NoInfer) => E): (a: A) => Either From f8660296895c8c8a87a67436dade7d118ae8ccdc Mon Sep 17 00:00:00 2001 From: Laure Retru-Chavastel Date: Thu, 6 Jun 2024 14:58:15 +0200 Subject: [PATCH 8/8] Update changeset --- .changeset/two-bananas-mix.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/two-bananas-mix.md b/.changeset/two-bananas-mix.md index e569ba4aba..7150f87283 100644 --- a/.changeset/two-bananas-mix.md +++ b/.changeset/two-bananas-mix.md @@ -1,5 +1,5 @@ --- -"effect": patch +"effect": minor --- Add Either.liftPredicate