From e3c7e32a4c46f9f16625e79ef69514d1253cf558 Mon Sep 17 00:00:00 2001 From: brekk Date: Sat, 27 Apr 2024 10:24:41 -1000 Subject: [PATCH] feat: add `neither`, `notBoth`, `xEither`, `xNeither` to Function --- prelude/__internal__/Function.mad | 58 ++++++++++++++++++++++++++ prelude/__internal__/Function.spec.mad | 45 ++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/prelude/__internal__/Function.mad b/prelude/__internal__/Function.mad index 478ad0545..da397e050 100644 --- a/prelude/__internal__/Function.mad +++ b/prelude/__internal__/Function.mad @@ -155,6 +155,19 @@ export flip = (f) => ((b, a) => f(a, b)) either :: (a -> Boolean) -> (a -> Boolean) -> a -> Boolean export either = (predA, predB, x) => predA(x) || predB(x) + +/** + * Functional "nor", given two predicates and a value, return true if neither predicate is true. + * + * @since 0.23.13 + * @example + * + * neither(equals("A"), equals("B"))("B") // false + * neither(equals("A"), equals("B"))("C") // true + */ +neither :: (a -> Boolean) -> (a -> Boolean) -> a -> Boolean +export neither = (predA, predB, x) => not(either(predA, predB, x)) + /** * Functional "and", given two predicates and a value, return true if both predicates are true. * @@ -168,3 +181,48 @@ export either = (predA, predB, x) => predA(x) || predB(x) */ both :: (a -> Boolean) -> (a -> Boolean) -> a -> Boolean export both = (predA, predB, x) => predA(x) && predB(x) + + +/** + * Functional "nand", given two predicates and a value, return true if either predicates are true, but not both. + * + * @since 0.23.13 + * @example + * + * import {lt} from "Compare" + * // as `lt` applies arguments backwards, 70 is greater than both 50 and 60 + * notBoth(lt(50), lt(60))(70) // false + * notBoth(lt(50), lt(60))(55) // true + */ +notBoth :: (a -> Boolean) -> (a -> Boolean) -> a -> Boolean +export notBoth = (predA, predB, x) => not(both(predA, predB, x)) + +/** + * Functional "xor", given two predicates and a value, return true if either predicate is true, but not both, nor neither. + * + * @since 0.23.13 + * @example + * + * import {lt} from "Compare" + * // as `lt` applies arguments backwards, 70 is greater than both 50 and 60 + * xEither(lt(50), lt(60))(70) // false + * xEither(lt(50), lt(60))(40) // false + * xEither(lt(50), lt(60))(55) // true + */ +xEither :: (a -> Boolean) -> (a -> Boolean) -> a -> Boolean +export xEither = (predA, predB, x) => predA(x) != predB(x) + +/** + * Functional "xnor", given two predicates and a value, return false if either predicate is false, but not both, nor neither. + * + * @since 0.13.0 + * @example + * + * import {lt} from "Compare" + * // as `lt` applies arguments backwards, 70 is greater than both 50 and 60 + * xEither(lt(50), lt(60))(70) // true + * xEither(lt(50), lt(60))(40) // true + * xEither(lt(50), lt(60))(55) // false + */ +xNeither :: (a -> Boolean) -> (a -> Boolean) -> a -> Boolean +export xNeither = (predA, predB, x) => not(xEither(predA, predB, x)) diff --git a/prelude/__internal__/Function.spec.mad b/prelude/__internal__/Function.spec.mad index ba0b88c4f..04cc6cec6 100644 --- a/prelude/__internal__/Function.spec.mad +++ b/prelude/__internal__/Function.spec.mad @@ -21,11 +21,15 @@ import { flip, identity, ifElse, + neither, not, + notBoth, notEquals, tailRec, unless, when, + xEither, + xNeither, } from "./Function" @@ -43,11 +47,15 @@ import { flip, identity, ifElse, + neither, not, + notBoth, notEquals, tailRec, unless, when, + xEither, + xNeither, } from "./Function" @@ -199,3 +207,40 @@ test( test("unless - true", () => assertEquals(unless(equals(5), (x) => x * 2, 5), 5)) test("unless - false", () => assertEquals(unless(equals(5), (x) => x * 2, 10), 20)) + +test( + "notBoth", + () => do { + _ <- assertEquals(notBoth(lt(50), lt(60))(70), false) + return assertEquals(notBoth(lt(50), lt(60))(55), true) + }, +) + +test( + "neither", + () => do { + notAB = neither(equals("A"), equals("B")) + _ <- assertEquals(notAB("A"), false) + _ <- assertEquals(notAB("B"), false) + return assertEquals(notAB("C"), true) + }, +) + +test( + "xEither", + () => do { + sclusy = xEither(lt(50), lt(60)) + _ <- assertEquals(sclusy(70), false) + _ <- assertEquals(sclusy(30), false) + return assertEquals(sclusy(55), true) + }, +) + +test( + "xNeither", + () => do { + _ <- assertEquals(xNeither(lt(50), lt(60))(70), true) + _ <- assertEquals(xNeither(lt(50), lt(60))(30), true) + return assertEquals(xNeither(lt(50), lt(60))(55), false) + }, +)