/** * This module provides the `Order` type class for defining total orderings on types. * An `Order` is a comparison function that returns `-1` (less than), `0` (equal), or `1` (greater than). * * Mental model: * - An `Order` is a pure function `(a: A, b: A) => Ordering` that compares two values * - The result `-1` means the first value is less than the second * - The result `0` means the values are equal according to this ordering * - The result `1` means the first value is greater than the second * - Orders must satisfy total ordering laws: totality (either `x <= y` or `y <= x`), antisymmetry (if `x <= y` and `y <= x` then `x == y`), and transitivity (if `x <= y` and `y <= z` then `x <= z`) * - Orders can be composed using {@link combine} and {@link combineAll} to create multi-criteria comparisons * - Orders can be transformed using {@link mapInput} to compare values by extracting a comparable property * - Built-in orders exist for common types: {@link Number}, {@link String}, {@link Boolean}, {@link BigInt}, {@link Date} * * Common tasks: * - Creating custom orders → {@link make} * - Using built-in orders → {@link Number}, {@link String}, {@link Boolean}, {@link BigInt}, {@link Date} * - Combining multiple orders → {@link combine}, {@link combineAll} * - Transforming orders → {@link mapInput} * - Comparing values → {@link isLessThan}, {@link isGreaterThan}, {@link isLessThanOrEqualTo}, {@link isGreaterThanOrEqualTo} * - Finding min/max → {@link min}, {@link max} * - Clamping values → {@link clamp}, {@link isBetween} * - Ordering collections → {@link Array}, {@link Tuple}, {@link Struct} * * Gotchas: * - `Order.Number` treats all `NaN` values as equal and less than any other number * - `Order.make` uses reference equality (`===`) as a shortcut: if `self === that`, it returns `0` without calling the comparison function * - `Order.Array` compares arrays element-by-element, then by length if all elements are equal; `Order.all` only compares elements up to the shorter array's length * - `Order.Tuple` requires a fixed-length tuple with matching order types; `Order.Array` works with variable-length arrays * - `Order.min` and `Order.max` return the first argument when values are equal * * Quickstart: * * **Example** (Basic Usage) * * ```ts * import { Order } from "effect" * * const result = Order.Number(5, 10) * console.log(result) // -1 (5 is less than 10) * * const isLessThan = Order.isLessThan(Order.Number)(5, 10) * console.log(isLessThan) // true * ``` * * See also: * - {@link Ordering} - The result type of comparisons * - {@link Reducer} - For combining orders in collections * * @since 2.0.0 */ import { dual } from "./Function.ts" import type { TypeLambda } from "./HKT.ts" import type { Ordering } from "./Ordering.ts" import * as Reducer from "./Reducer.ts" /** * Represents a total ordering for values of type `A`. * * When to use this: * - When you need to define how values of a type should be compared * - When implementing sorting, searching, or ordered data structures * - When composing multiple comparison criteria * * Behavior: * - Pure function: does not mutate inputs or have side effects * - Returns `-1` if the first value is less than the second * - Returns `0` if the values are equal according to this ordering * - Returns `1` if the first value is greater than the second * - Must satisfy total ordering laws (totality, antisymmetry, transitivity) * * **Example** (Custom Order) * * ```ts * import { Order } from "effect" * * const byAge: Order.Order<{ name: string; age: number }> = (self, that) => { * if (self.age < that.age) return -1 * if (self.age > that.age) return 1 * return 0 * } * * const person1 = { name: "Alice", age: 30 } * const person2 = { name: "Bob", age: 25 } * console.log(byAge(person1, person2)) // 1 * ``` * * See also: * - {@link make} - Create an order from a comparison function * - {@link Ordering} - The result type of comparisons * * @category type class * @since 2.0.0 */ export interface Order { (self: A, that: A): Ordering } /** * Type lambda for the `Order` type class, used internally for higher-kinded type operations. * * When to use this: * - When working with type-level operations that require higher-kinded types * - When implementing generic type classes that work with orders * * Behavior: * - Type-level only: no runtime representation * - Used internally by the Effect type system * * @category type lambdas * @since 2.0.0 */ export interface OrderTypeLambda extends TypeLambda { readonly type: Order } /** * Creates a new `Order` instance from a comparison function. * * When to use this: * - When creating a custom order for a type that doesn't have a built-in order * - When you need fine-grained control over comparison logic * - When implementing orders for complex types * * Behavior: * - Pure function: does not mutate inputs * - Uses reference equality (`===`) as a shortcut: if `self === that`, returns `0` without calling the comparison function * - The comparison function should return `-1`, `0`, or `1` based on the comparison result * - The returned order satisfies total ordering laws if the comparison function does * * **Example** (Creating an Order) * * ```ts * import { Order } from "effect" * * const byAge = Order.make<{ name: string; age: number }>((self, that) => { * if (self.age < that.age) return -1 * if (self.age > that.age) return 1 * return 0 * }) * * console.log(byAge({ name: "Alice", age: 30 }, { name: "Bob", age: 25 })) // 1 * console.log(byAge({ name: "Alice", age: 25 }, { name: "Bob", age: 30 })) // -1 * ``` * * See also: * - {@link mapInput} - Transform an order by mapping the input type * - {@link combine} - Combine multiple orders * * @category constructors * @since 2.0.0 */ export function make( compare: (self: A, that: A) => -1 | 0 | 1 ): Order { return (self, that) => self === that ? 0 : compare(self, that) } /** * An `Order` instance for strings that compares them lexicographically using JavaScript's `<` operator. * * When to use this: * - When comparing strings alphabetically * - When sorting string collections * - As a base for creating orders on types containing strings * * Behavior: * - Pure function: does not mutate inputs * - Uses lexicographic (dictionary) ordering * - Empty string is less than any non-empty string * - Comparison is case-sensitive * * **Example** (String Ordering) * * ```ts * import { Order } from "effect" * * console.log(Order.String("apple", "banana")) // -1 * console.log(Order.String("banana", "apple")) // 1 * console.log(Order.String("apple", "apple")) // 0 * ``` * * See also: * - {@link mapInput} - Use this order to compare objects by a string property * - {@link Struct} - Combine with other orders for struct comparison * * @category instances * @since 4.0.0 */ export const String: Order = make((self, that) => self < that ? -1 : 1) /** * An `Order` instance for numbers that compares them numerically. * * When to use this: * - When comparing numbers for sorting or searching * - As a base for creating orders on types containing numbers * - When implementing numeric comparisons in data structures * * Behavior: * - Pure function: does not mutate inputs * - `0` is considered equal to `-0` * - All `NaN` values are considered equal to each other * - Any `NaN` is considered less than any non-NaN number * - Uses standard numeric comparison for all other values * * **Example** (Number Ordering) * * ```ts * import { Order } from "effect" * * console.log(Order.Number(1, 1)) // 0 * console.log(Order.Number(1, 2)) // -1 * console.log(Order.Number(2, 1)) // 1 * * console.log(Order.Number(0, -0)) // 0 * console.log(Order.Number(NaN, 1)) // -1 * ``` * * See also: * - {@link mapInput} - Use this order to compare objects by a number property * - {@link BigInt} - For bigint comparisons * * @category instances * @since 4.0.0 */ export const Number: Order = make((self, that) => { if (globalThis.Number.isNaN(self) && globalThis.Number.isNaN(that)) return 0 if (globalThis.Number.isNaN(self)) return -1 // NaN < any number if (globalThis.Number.isNaN(that)) return 1 // any number > NaN return self < that ? -1 : 1 }) /** * An `Order` instance for booleans where `false` is considered less than `true`. * * When to use this: * - When comparing booleans for sorting or searching * - As a base for creating orders on types containing booleans * - When implementing boolean-based comparisons * * Behavior: * - Pure function: does not mutate inputs * - `false` is less than `true` * - Equal values return `0` * * **Example** (Boolean Ordering) * * ```ts * import { Order } from "effect" * * console.log(Order.Boolean(false, true)) // -1 * console.log(Order.Boolean(true, false)) // 1 * console.log(Order.Boolean(true, true)) // 0 * ``` * * See also: * - {@link mapInput} - Use this order to compare objects by a boolean property * * @category instances * @since 4.0.0 */ export const Boolean: Order = make((self, that) => self < that ? -1 : 1) /** * An `Order` instance for bigints that compares them numerically. * * When to use this: * - When comparing bigint values for sorting or searching * - As a base for creating orders on types containing bigints * - When working with large integers that exceed number precision * * Behavior: * - Pure function: does not mutate inputs * - Uses standard numeric comparison for bigint values * - Handles arbitrarily large integers * * **Example** (BigInt Ordering) * * ```ts * import { Order } from "effect" * * console.log(Order.BigInt(1n, 2n)) // -1 * console.log(Order.BigInt(2n, 1n)) // 1 * console.log(Order.BigInt(1n, 1n)) // 0 * ``` * * See also: * - {@link Number} - For regular number comparisons * - {@link mapInput} - Use this order to compare objects by a bigint property * * @category instances * @since 4.0.0 */ export const BigInt: Order = make((self, that) => self < that ? -1 : 1) /** * Creates a new `Order` that reverses the comparison order of the input `Order`. * * When to use this: * - When you need descending order instead of ascending * - When reversing an existing order without modifying the original * - When creating orders that compare in the opposite direction * * Behavior: * - Pure function: does not mutate inputs * - Returns a new order that swaps the arguments before comparison * - If the original order returns `-1`, the flipped order returns `1`, and vice versa * - Equal comparisons remain `0` * * **Example** (Reversing Order) * * ```ts * import { Order } from "effect" * * const flip = Order.flip(Order.Number) * * console.log(flip(1, 2)) // 1 * console.log(flip(2, 1)) // -1 * console.log(flip(1, 1)) // 0 * ``` * * See also: * - {@link combine} - Combine orders for multi-criteria comparison * * @category combinators * @since 2.0.0 */ export function flip(O: Order): Order { return make((self, that) => O(that, self)) } /** * Combines two `Order` instances to create a new `Order` that first compares using the first `Order`, * and if the values are equal, then compares using the second `Order`. * * When to use this: * - When you need multi-criteria comparison (e.g., sort by age, then by name) * - When creating composite orders from simpler orders * - When implementing lexicographic ordering * * Behavior: * - Pure function: does not mutate inputs * - First applies the first order; if the result is non-zero, returns that result * - If the first order returns `0` (equal), applies the second order * - Returns the first non-zero result, or `0` if both orders return `0` * * **Example** (Combining Orders) * * ```ts * import { Order } from "effect" * * const byAge = Order.mapInput( * Order.Number, * (person: { name: string; age: number }) => person.age * ) * const byName = Order.mapInput( * Order.String, * (person: { name: string; age: number }) => person.name * ) * const byAgeAndName = Order.combine(byAge, byName) * * const person1 = { name: "Alice", age: 30 } * const person2 = { name: "Bob", age: 30 } * const person3 = { name: "Charlie", age: 25 } * * console.log(byAgeAndName(person1, person2)) // -1 (Same age, Alice < Bob) * console.log(byAgeAndName(person1, person3)) // 1 (Alice (30) > Charlie (25)) * ``` * * See also: * - {@link combineAll} - Combine multiple orders from a collection * - {@link mapInput} - Transform orders to work with different types * * @category combining * @since 2.0.0 */ export const combine: { /** * Combines two `Order` instances to create a new `Order` that first compares using the first `Order`, * and if the values are equal, then compares using the second `Order`. * * When to use this: * - When you need multi-criteria comparison (e.g., sort by age, then by name) * - When creating composite orders from simpler orders * - When implementing lexicographic ordering * * Behavior: * - Pure function: does not mutate inputs * - First applies the first order; if the result is non-zero, returns that result * - If the first order returns `0` (equal), applies the second order * - Returns the first non-zero result, or `0` if both orders return `0` * * **Example** (Combining Orders) * * ```ts * import { Order } from "effect" * * const byAge = Order.mapInput( * Order.Number, * (person: { name: string; age: number }) => person.age * ) * const byName = Order.mapInput( * Order.String, * (person: { name: string; age: number }) => person.name * ) * const byAgeAndName = Order.combine(byAge, byName) * * const person1 = { name: "Alice", age: 30 } * const person2 = { name: "Bob", age: 30 } * const person3 = { name: "Charlie", age: 25 } * * console.log(byAgeAndName(person1, person2)) // -1 (Same age, Alice < Bob) * console.log(byAgeAndName(person1, person3)) // 1 (Alice (30) > Charlie (25)) * ``` * * See also: * - {@link combineAll} - Combine multiple orders from a collection * - {@link mapInput} - Transform orders to work with different types * * @category combining * @since 2.0.0 */ (that: Order): (self: Order) => Order /** * Combines two `Order` instances to create a new `Order` that first compares using the first `Order`, * and if the values are equal, then compares using the second `Order`. * * When to use this: * - When you need multi-criteria comparison (e.g., sort by age, then by name) * - When creating composite orders from simpler orders * - When implementing lexicographic ordering * * Behavior: * - Pure function: does not mutate inputs * - First applies the first order; if the result is non-zero, returns that result * - If the first order returns `0` (equal), applies the second order * - Returns the first non-zero result, or `0` if both orders return `0` * * **Example** (Combining Orders) * * ```ts * import { Order } from "effect" * * const byAge = Order.mapInput( * Order.Number, * (person: { name: string; age: number }) => person.age * ) * const byName = Order.mapInput( * Order.String, * (person: { name: string; age: number }) => person.name * ) * const byAgeAndName = Order.combine(byAge, byName) * * const person1 = { name: "Alice", age: 30 } * const person2 = { name: "Bob", age: 30 } * const person3 = { name: "Charlie", age: 25 } * * console.log(byAgeAndName(person1, person2)) // -1 (Same age, Alice < Bob) * console.log(byAgeAndName(person1, person3)) // 1 (Alice (30) > Charlie (25)) * ``` * * See also: * - {@link combineAll} - Combine multiple orders from a collection * - {@link mapInput} - Transform orders to work with different types * * @category combining * @since 2.0.0 */ (self: Order, that: Order): Order } = dual(2, (self: Order, that: Order): Order => make((a1, a2) => { const out = self(a1, a2) if (out !== 0) { return out } return that(a1, a2) })) /** * Creates an `Order` that considers all values as equal. * * When to use this: * - When you need an order that doesn't distinguish between values * - As a default or fallback order when no meaningful comparison exists * - When implementing optional ordering where equality is sufficient * * Behavior: * - Pure function: does not mutate inputs * - Always returns `0` regardless of input values * - Useful as a neutral element in order composition * * **Example** (Always Equal Order) * * ```ts * import { Order } from "effect" * * const alwaysEqualOrder = Order.alwaysEqual() * * console.log(alwaysEqualOrder(1, 2)) // 0 * console.log(alwaysEqualOrder(2, 1)) // 0 * console.log(alwaysEqualOrder(1, 1)) // 0 * ``` * * See also: * - {@link combine} - Combine with other orders * * @category constructors * @since 4.0.0 */ export function alwaysEqual(): Order { return make(() => 0) } /** * Combines all `Order` instances in the provided collection into a single `Order`. * The resulting `Order` compares using each `Order` in sequence until a non-zero result is found. * * When to use this: * - When you have a variable number of orders to combine * - When combining orders from a collection or array * - When implementing dynamic multi-criteria sorting * * Behavior: * - Pure function: does not mutate inputs * - Applies orders in iteration order * - Returns the first non-zero result from any order * - Returns `0` only if all orders return `0` * - Short-circuits on the first non-zero result * * **Example** (Combining Multiple Orders) * * ```ts * import { Order } from "effect" * * const byAge = Order.mapInput( * Order.Number, * (person: { name: string; age: number }) => person.age * ) * const byName = Order.mapInput( * Order.String, * (person: { name: string; age: number }) => person.name * ) * * const combinedOrder = Order.combineAll([byAge, byName]) * * const person1 = { name: "Alice", age: 30 } * const person2 = { name: "Bob", age: 30 } * * console.log(combinedOrder(person1, person2)) // -1 (Same age, Alice < Bob) * ``` * * See also: * - {@link combine} - Combine two orders * - {@link makeReducer} - Create a reducer for combining orders * * @category combining * @since 2.0.0 */ export function combineAll(collection: Iterable>): Order { return make((a1, a2) => { let out: Ordering = 0 for (const O of collection) { out = O(a1, a2) if (out !== 0) { return out } } return out }) } /** * Transforms an `Order` on type `A` into an `Order` on type `B` by providing a function that * maps values of type `B` to values of type `A`. * * When to use this: * - When you have an order for a property type and want to compare objects by that property * - When extracting a comparable value from a complex type * - When creating orders for types that contain comparable values * * Behavior: * - Pure function: does not mutate inputs * - Applies the mapping function to both values before comparison * - The mapping function should be pure and not have side effects * - Preserves the ordering properties of the original order * * **Example** (Mapping Input) * * ```ts * import { Order } from "effect" * * const byLength = Order.mapInput(Order.Number, (s: string) => s.length) * * console.log(byLength("a", "bb")) // -1 * console.log(byLength("bb", "a")) // 1 * console.log(byLength("aa", "bb")) // 0 * ``` * * See also: * - {@link combine} - Combine mapped orders for multi-criteria comparison * - {@link Struct} - Create orders for structs with multiple fields * * @category mapping * @since 2.0.0 */ export const mapInput: { /** * Transforms an `Order` on type `A` into an `Order` on type `B` by providing a function that * maps values of type `B` to values of type `A`. * * When to use this: * - When you have an order for a property type and want to compare objects by that property * - When extracting a comparable value from a complex type * - When creating orders for types that contain comparable values * * Behavior: * - Pure function: does not mutate inputs * - Applies the mapping function to both values before comparison * - The mapping function should be pure and not have side effects * - Preserves the ordering properties of the original order * * **Example** (Mapping Input) * * ```ts * import { Order } from "effect" * * const byLength = Order.mapInput(Order.Number, (s: string) => s.length) * * console.log(byLength("a", "bb")) // -1 * console.log(byLength("bb", "a")) // 1 * console.log(byLength("aa", "bb")) // 0 * ``` * * See also: * - {@link combine} - Combine mapped orders for multi-criteria comparison * - {@link Struct} - Create orders for structs with multiple fields * * @category mapping * @since 2.0.0 */ (f: (b: B) => A): (self: Order) => Order /** * Transforms an `Order` on type `A` into an `Order` on type `B` by providing a function that * maps values of type `B` to values of type `A`. * * When to use this: * - When you have an order for a property type and want to compare objects by that property * - When extracting a comparable value from a complex type * - When creating orders for types that contain comparable values * * Behavior: * - Pure function: does not mutate inputs * - Applies the mapping function to both values before comparison * - The mapping function should be pure and not have side effects * - Preserves the ordering properties of the original order * * **Example** (Mapping Input) * * ```ts * import { Order } from "effect" * * const byLength = Order.mapInput(Order.Number, (s: string) => s.length) * * console.log(byLength("a", "bb")) // -1 * console.log(byLength("bb", "a")) // 1 * console.log(byLength("aa", "bb")) // 0 * ``` * * See also: * - {@link combine} - Combine mapped orders for multi-criteria comparison * - {@link Struct} - Create orders for structs with multiple fields * * @category mapping * @since 2.0.0 */ (self: Order, f: (b: B) => A): Order } = dual( 2, (self: Order, f: (b: B) => A): Order => make((b1, b2) => self(f(b1), f(b2))) ) /** * An `Order` instance for `Date` objects that compares them chronologically by their timestamp. * * When to use this: * - When comparing dates for sorting or searching * - As a base for creating orders on types containing dates * - When implementing time-based comparisons * * Behavior: * - Pure function: does not mutate inputs * - Compares dates by their underlying timestamp (milliseconds since epoch) * - Earlier dates are less than later dates * - Invalid dates are compared as if they were valid (uses `getTime()` result) * * **Example** (Date Ordering) * * ```ts * import { Order } from "effect" * * const date1 = new Date("2023-01-01") * const date2 = new Date("2023-01-02") * * console.log(Order.Date(date1, date2)) // -1 * console.log(Order.Date(date2, date1)) // 1 * console.log(Order.Date(date1, date1)) // 0 * ``` * * See also: * - {@link mapInput} - Use this order to compare objects by a date property * * @category instances * @since 2.0.0 */ export const Date: Order = mapInput(Number, (date) => date.getTime()) /** * Creates an `Order` for a tuple type based on orders for each element. * * When to use this: * - When comparing tuples with different types for each position * - When you need type-safe tuple ordering * - When working with fixed-length heterogeneous collections * * Behavior: * - Pure function: does not mutate inputs * - Compares tuples element-by-element using the corresponding order * - Stops at the first non-zero comparison result * - Requires tuples to have the same length as the order collection * - Returns `0` if all elements are equal * * **Example** (Tuple Ordering) * * ```ts * import { Order } from "effect" * * const tupleOrder = Order.Tuple([Order.Number, Order.String]) * * console.log(tupleOrder([1, "a"], [2, "b"])) // -1 * console.log(tupleOrder([1, "b"], [1, "a"])) // 1 * console.log(tupleOrder([1, "a"], [1, "a"])) // 0 * ``` * * See also: * - {@link Array} - Compare arrays with length consideration * * @category combinators * @since 4.0.0 */ export function Tuple>>( elements: Elements ): Order<{ readonly [I in keyof Elements]: [Elements[I]] extends [Order] ? A : never }> { return make((self, that) => { const len = elements.length for (let i = 0; i < len; i++) { const o = elements[i](self[i], that[i]) if (o !== 0) { return o } } return 0 }) } /** * @since 4.0.0 */ function Array_(O: Order): Order> { return make((self, that) => { const aLen = self.length const bLen = that.length const len = Math.min(aLen, bLen) for (let i = 0; i < len; i++) { const o = O(self[i], that[i]) if (o !== 0) { return o } } return Number(aLen, bLen) }) } export { /** * Creates an `Order` for arrays by applying the given `Order` to each element, then comparing by length if all elements are equal. * * When to use this: * - When comparing arrays of the same element type * - When you want shorter arrays to be considered less than longer arrays * - When sorting collections of arrays * * Behavior: * - Pure function: does not mutate inputs * - Compares arrays element-by-element using the provided order * - Stops at the first non-zero comparison result * - If all elements are equal, shorter arrays are less than longer arrays * - Returns `0` only if arrays have the same length and all elements are equal * * **Example** (Array Element Ordering) * * ```ts * import { Order } from "effect" * * const arrayOrder = Order.Array(Order.Number) * * console.log(arrayOrder([1, 2], [1, 3])) // -1 * console.log(arrayOrder([1, 2], [1, 2, 3])) // -1 (shorter array is less) * console.log(arrayOrder([1, 2, 3], [1, 2])) // 1 (longer array is greater) * console.log(arrayOrder([1, 2], [1, 2])) // 0 * ``` * * See also: * - {@link Tuple} - Type-safe tuple ordering * * @category combinators * @since 4.0.0 */ Array_ as Array } /** * Creates an `Order` for structs by applying the given `Order`s to each property in sequence. * * When to use this: * - When comparing objects with multiple properties * - When you need multi-field comparison for structs * - When creating orders for complex data types * * Behavior: * - Pure function: does not mutate inputs * - Compares structs field-by-field in the order of keys in the fields object * - Stops at the first non-zero comparison result * - Returns `0` only if all fields are equal * - Field order matters: earlier fields take precedence * * **Example** (Struct Ordering) * * ```ts * import { Order } from "effect" * * const personOrder = Order.Struct({ * name: Order.String, * age: Order.Number * }) * * const person1 = { name: "Alice", age: 30 } * const person2 = { name: "Bob", age: 25 } * const person3 = { name: "Alice", age: 25 } * * console.log(personOrder(person1, person2)) // -1 (Alice < Bob) * console.log(personOrder(person1, person3)) // 1 (same name, 30 > 25) * console.log(personOrder(person1, person1)) // 0 * ``` * * See also: * - {@link combine} - Combine orders manually * - {@link mapInput} - Extract and compare by a single property * * @category combinators * @since 4.0.0 */ export function Struct }>( fields: R ): Order<{ [K in keyof R]: [R[K]] extends [Order] ? A : never }> { const keys = Object.keys(fields) return make((self, that) => { for (const key of keys) { const o = fields[key](self[key], that[key]) if (o !== 0) { return o } } return 0 }) } /** * Tests whether one value is strictly less than another according to the given order. * * When to use this: * - When you need a boolean predicate instead of an ordering result * - When checking if a value is less than another in conditional logic * - When implementing range checks or comparisons * * Behavior: * - Pure function: does not mutate inputs * - Returns `true` if the order returns `-1` (first value is less than second) * - Returns `false` for equal or greater values * - Supports curried and uncurried call styles * * **Example** (Less Than) * * ```ts * import { Order } from "effect" * * const isLessThanNumber = Order.isLessThan(Order.Number) * * console.log(isLessThanNumber(1, 2)) // true * console.log(isLessThanNumber(2, 1)) // false * console.log(isLessThanNumber(1, 1)) // false * ``` * * See also: * - {@link isLessThanOrEqualTo} - Non-strict less than or equal * - {@link isGreaterThan} - Strict greater than * * @category predicates * @since 2.0.0 */ export const isLessThan = (O: Order): { (that: A): (self: A) => boolean (self: A, that: A): boolean } => dual(2, (self: A, that: A) => O(self, that) === -1) /** * Tests whether one value is strictly greater than another according to the given order. * * When to use this: * - When you need a boolean predicate instead of an ordering result * - When checking if a value is greater than another in conditional logic * - When implementing range checks or comparisons * * Behavior: * - Pure function: does not mutate inputs * - Returns `true` if the order returns `1` (first value is greater than second) * - Returns `false` for equal or lesser values * - Supports curried and uncurried call styles * * **Example** (Greater Than) * * ```ts * import { Order } from "effect" * * const isGreaterThanNumber = Order.isGreaterThan(Order.Number) * * console.log(isGreaterThanNumber(2, 1)) // true * console.log(isGreaterThanNumber(1, 2)) // false * console.log(isGreaterThanNumber(1, 1)) // false * ``` * * See also: * - {@link isGreaterThanOrEqualTo} - Non-strict greater than or equal * - {@link isLessThan} - Strict less than * * @category predicates * @since 2.0.0 */ export const isGreaterThan = (O: Order): { (that: A): (self: A) => boolean (self: A, that: A): boolean } => dual(2, (self: A, that: A) => O(self, that) === 1) /** * Tests whether one value is less than or equal to another according to the given order. * * When to use this: * - When you need a boolean predicate for non-strict comparison * - When checking if a value is within a range (inclusive lower bound) * - When implementing inclusive comparisons * * Behavior: * - Pure function: does not mutate inputs * - Returns `true` if the order returns `-1` or `0` (less than or equal) * - Returns `false` only if the order returns `1` (greater than) * - Supports curried and uncurried call styles * * **Example** (Less Than Or Equal) * * ```ts * import { Order } from "effect" * * const isLessThanOrEqualToNumber = Order.isLessThanOrEqualTo(Order.Number) * * console.log(isLessThanOrEqualToNumber(1, 2)) // true * console.log(isLessThanOrEqualToNumber(1, 1)) // true * console.log(isLessThanOrEqualToNumber(2, 1)) // false * ``` * * See also: * - {@link isLessThan} - Strict less than * - {@link isGreaterThan} - Strict greater than * * @category predicates * @since 2.0.0 */ export const isLessThanOrEqualTo = (O: Order): { (that: A): (self: A) => boolean (self: A, that: A): boolean } => dual(2, (self: A, that: A) => O(self, that) !== 1) /** * Tests whether one value is greater than or equal to another according to the given order. * * When to use this: * - When you need a boolean predicate for non-strict comparison * - When checking if a value is within a range (inclusive upper bound) * - When implementing inclusive comparisons * * Behavior: * - Pure function: does not mutate inputs * - Returns `true` if the order returns `1` or `0` (greater than or equal) * - Returns `false` only if the order returns `-1` (less than) * - Supports curried and uncurried call styles * * **Example** (Greater Than Or Equal) * * ```ts * import { Order } from "effect" * * const isGreaterThanOrEqualToNumber = Order.isGreaterThanOrEqualTo(Order.Number) * * console.log(isGreaterThanOrEqualToNumber(2, 1)) // true * console.log(isGreaterThanOrEqualToNumber(1, 1)) // true * console.log(isGreaterThanOrEqualToNumber(1, 2)) // false * ``` * * See also: * - {@link isGreaterThan} - Strict greater than * - {@link isLessThanOrEqualTo} - Less than or equal * * @category predicates * @since 2.0.0 */ export const isGreaterThanOrEqualTo = (O: Order): { (that: A): (self: A) => boolean (self: A, that: A): boolean } => dual(2, (self: A, that: A) => O(self, that) !== -1) /** * Returns the minimum of two values according to the given order. If they are equal, returns the first argument. * * When to use this: * - When you need to find the smaller of two values * - When implementing min/max operations * - When selecting values based on ordering * * Behavior: * - Pure function: does not mutate inputs * - Returns the value that compares as less than or equal to the other * - If values are equal, returns the first argument * - Supports curried and uncurried call styles * * **Example** (Minimum Value) * * ```ts * import { Order } from "effect" * * const minNumber = Order.min(Order.Number) * * console.log(minNumber(1, 2)) // 1 * console.log(minNumber(2, 1)) // 1 * console.log(minNumber(1, 1)) // 1 * ``` * * See also: * - {@link max} - Maximum of two values * - {@link clamp} - Clamp a value between min and max * * @category comparisons * @since 2.0.0 */ export const min = (O: Order): { (that: A): (self: A) => A (self: A, that: A): A } => dual(2, (self: A, that: A) => self === that || O(self, that) < 1 ? self : that) /** * Returns the maximum of two values according to the given order. If they are equal, returns the first argument. * * When to use this: * - When you need to find the larger of two values * - When implementing min/max operations * - When selecting values based on ordering * * Behavior: * - Pure function: does not mutate inputs * - Returns the value that compares as greater than or equal to the other * - If values are equal, returns the first argument * - Supports curried and uncurried call styles * * **Example** (Maximum Value) * * ```ts * import { Order } from "effect" * * const maxNumber = Order.max(Order.Number) * * console.log(maxNumber(1, 2)) // 2 * console.log(maxNumber(2, 1)) // 2 * console.log(maxNumber(1, 1)) // 1 * ``` * * See also: * - {@link min} - Minimum of two values * - {@link clamp} - Clamp a value between min and max * * @category comparisons * @since 2.0.0 */ export const max = (O: Order): { (that: A): (self: A) => A (self: A, that: A): A } => dual(2, (self: A, that: A) => self === that || O(self, that) > -1 ? self : that) /** * Clamps a value between a minimum and a maximum according to the given order. * * When to use this: * - When you need to restrict a value to a specific range * - When implementing bounds checking and normalization * - When ensuring values stay within valid ranges * * Behavior: * - Pure function: does not mutate inputs * - Returns the value if it's between minimum and maximum (inclusive) * - Returns minimum if the value is less than minimum * - Returns maximum if the value is greater than maximum * - Supports curried and uncurried call styles * - Requires that minimum <= maximum according to the order * * **Example** (Clamping Values) * * ```ts * import { Order } from "effect" * * const clamp = Order.clamp(Order.Number)({ minimum: 1, maximum: 5 }) * * console.log(clamp(3)) // 3 * console.log(clamp(0)) // 1 * console.log(clamp(6)) // 5 * ``` * * See also: * - {@link min} - Minimum of two values * - {@link max} - Maximum of two values * - {@link isBetween} - Check if a value is within a range * * @category comparisons * @since 2.0.0 */ export const clamp = (O: Order): { (options: { minimum: A maximum: A }): (self: A) => A (self: A, options: { minimum: A maximum: A }): A } => dual( 2, (self: A, options: { minimum: A maximum: A }): A => min(O)(options.maximum, max(O)(options.minimum, self)) ) /** * Tests whether a value is between a minimum and a maximum (inclusive) according to the given order. * * When to use this: * - When validating that a value is within a valid range * - When implementing range checks for bounds validation * - When filtering or selecting values within a range * * Behavior: * - Pure function: does not mutate inputs * - Returns `true` if the value is greater than or equal to minimum and less than or equal to maximum * - Returns `false` if the value is outside the range * - Supports curried and uncurried call styles * - Both bounds are inclusive * * **Example** (Checking Range) * * ```ts * import { Order } from "effect" * * const betweenNumber = Order.isBetween(Order.Number) * * console.log(betweenNumber(5, { minimum: 1, maximum: 10 })) // true * console.log(betweenNumber(1, { minimum: 1, maximum: 10 })) // true * console.log(betweenNumber(10, { minimum: 1, maximum: 10 })) // true * console.log(betweenNumber(0, { minimum: 1, maximum: 10 })) // false * console.log(betweenNumber(11, { minimum: 1, maximum: 10 })) // false * ``` * * See also: * - {@link clamp} - Clamp a value to a range * - {@link isLessThanOrEqualTo} - Less than or equal check * - {@link isGreaterThanOrEqualTo} - Greater than or equal check * * @category predicates * @since 4.0.0 */ export const isBetween = (O: Order): { (options: { minimum: A maximum: A }): (self: A) => boolean (self: A, options: { minimum: A maximum: A }): boolean } => dual( 2, (self: A, options: { minimum: A maximum: A }): boolean => !isLessThan(O)(self, options.minimum) && !isGreaterThan(O)(self, options.maximum) ) /** * Creates a `Reducer` for combining `Order` instances, useful for aggregating orders in collections. * * When to use this: * - When you need to combine multiple orders from a collection using reducer patterns * - When implementing fold operations over collections of orders * - When working with reducers that operate on orders * * Behavior: * - Returns a reducer that combines orders using {@link combine} * - Uses {@link alwaysEqual} as the identity element (returns `0` for empty collections) * - Uses {@link combineAll} for combining collections of orders * - The reducer can be used with fold operations on collections * * **Example** (Creating a Reducer) * * ```ts * import { Order } from "effect" * * const reducer = Order.makeReducer() * const orders = [Order.Number, Order.flip(Order.Number)] * * const combined = reducer.combineAll(orders) * console.log(combined(1, 2)) // -1 (uses first order) * ``` * * See also: * - {@link combine} - Combine two orders * - {@link combineAll} - Combine multiple orders * - {@link Reducer} - Reducer type for collection operations * * @category utilities * @since 4.0.0 */ export function makeReducer() { return Reducer.make>( combine, () => 0, combineAll ) }