/**
* The Predicate type represents unary functions that return boolean values.
* Typically, these functions indicate the existence of some quality for the
* values passed as inputs. Some simple examples of Predicates are postive for
* numbers, non-null values, etc.
*
* @module Predicate
* @since 2.0.0
*/
import type { In, Kind } from "./kind.ts";
import type { Combinable } from "./combinable.ts";
import type { Initializable } from "./initializable.ts";
import { flow } from "./fn.ts";
/**
* The Predicate type is a function that takes some
* value of type A and returns boolean, indicating
* that a property is true or false for the value A.
*
* @example
* ```ts
* import type { Predicate } from "./predicate.ts";
* import * as O from "./option.ts";
*
* function fromPredicate(predicate: Predicate) {
* return (a: A): O.Option => predicate(a)
* ? O.some(a) : O.none;
* }
*
* function isPositive(n: number): boolean {
* return n > 0;
* }
*
* const isPos = fromPredicate(isPositive);
*
* const resultSome = isPos(1); // Some(1)
* const resultNone = isPos(-1); // None
* ```
*
* @since 2.0.0
*/
export type Predicate = (a: A) => boolean;
/**
* Specifies Predicate as a Higher Kinded Type, with
* contravariant parameter A corresponding to the 0th
* index of any Substitutions.
*
* @since 2.0.0
*/
export interface KindPredicate extends Kind {
readonly kind: Predicate>;
}
/**
* Create a Predicate using a Predicate and a function that takes
* a type L and returns a type D. This maps over the input value of
* the predicate.
*
* @example
* ```ts
* import { premap } from "./predicate.ts";
* import { pipe } from "./fn.ts";
*
* const isGreaterThan3 = (n: number) => n > 3;
* const isLongerThan3 = pipe(
* isGreaterThan3,
* premap((s: string) => s.length),
* );
*
* const result1 = isLongerThan3("Hello"); // true
* const result2 = isLongerThan3("Hi"); // false
* ```
*
* @since 2.0.0
*/
export function premap(
fia: (i: I) => A,
): (ua: Predicate) => Predicate {
return (ua) => flow(fia, ua);
}
/**
* Negates the result of an existing Predicate.
*
* @example
* ```ts
* import { not } from "./predicate.ts";
*
* const isPositive = (n: number) => n > 0;
* const isZeroOrNegative = not(isPositive);
*
* const result1 = isZeroOrNegative(1); // false
* const result2 = isZeroOrNegative(0); // true
* const result3 = isZeroOrNegative(-1); // true
* ```
*
* @since 2.0.0
*/
export function not(predicate: Predicate): Predicate {
return (a) => !predicate(a);
}
/**
* Creates the union of two predicates, returning true if either
* predicate returns true.
*
* @example
* ```ts
* import { or } from "./predicate.ts";
* import { string, number } from "./refinement.ts";
* import { pipe } from "./fn.ts";
*
* // A Refinement is also a Predicate
* const stringOrNumber = pipe(
* string,
* or(number),
* );
*
* const result1 = stringOrNumber("Hello"); // true
* const result2 = stringOrNumber(1); // true
* const result3 = stringOrNumber({}); // false
* ```
*
* @since 2.0.0
*/
export function or(
second: Predicate,
): (first: Predicate) => Predicate {
return (first) => (a) => first(a) || second(a);
}
/**
* Creates the intersection of two predicates, returning true if both
* predicates return true.
*
* @example
* ```ts
* import { and } from "./predicate.ts";
* import { pipe } from "./fn.ts";
*
* const isPositive = (n: number) => n > 0;
* const isInteger = (n: number) => Number.isInteger(n);
*
* const isPositiveInteger = pipe(
* isPositive,
* and(isInteger),
* );
*
* const result1 = isPositiveInteger(1); // true
* const result2 = isPositiveInteger(100); // true
* const result3 = isPositiveInteger(-1); // false
* ```
*
* @since 2.0.0
*/
export function and(
second: Predicate,
): (first: Predicate) => Predicate {
return (first) => (a) => first(a) && second(a);
}
/**
* @since 2.0.0
*/
export function getCombinableAny(): Combinable> {
return { combine: or };
}
/**
* @since 2.0.0
*/
export function getCombinableAll(): Combinable> {
return { combine: and };
}
/**
* Get a Initializable> for any type A that combines using the
* Predicate or function.
*
* @example
* ```ts
* import { getInitializableAny } from "./predicate.ts";
* import { pipe } from "./fn.ts";
*
* const { combine } = getInitializableAny();
*
* const lessThanZero = (n: number) => n < 0;
* const greaterThanFifty = (n: number) => n > 50;
*
* const notBetweenZeroAndFifty = pipe(
* lessThanZero,
* combine(greaterThanFifty),
* );
*
* const result1 = notBetweenZeroAndFifty(10); // false
* const result2 = notBetweenZeroAndFifty(-10); // true
* const result3 = notBetweenZeroAndFifty(100); // true
* ```
*
* @since 2.0.0
*/
export function getInitializableAny(): Initializable> {
return {
combine: or,
init: () => () => false,
};
}
/**
* Get a Initializable> for any type A that combines using the
* Predicate and function.
*
* @example
* ```ts
* import { getInitializableAll } from "./predicate.ts";
* import { pipe } from "./fn.ts";
*
* const { combine } = getInitializableAll();
*
* const greaterThanZero = (n: number) => n > 0;
* const lessThanFifty = (n: number) => n < 50;
*
* const betweenZeroAndFifty = pipe(
* greaterThanZero,
* combine(lessThanFifty)
* );
*
* const result1 = betweenZeroAndFifty(10); // true
* const result2 = betweenZeroAndFifty(-10); // false
* const result3 = betweenZeroAndFifty(100); // false
* ```
*
* @since 2.0.0
*/
export function getInitializableAll(): Initializable> {
return {
combine: and,
init: () => () => true,
};
}