UNPKG

rivo

Version:

🤖 The ultimate library you need for composable type-level programming in TypeScript, powered by HKT.

242 lines (232 loc) • 6.99 kB
import type CompareFn from "./Compare"; import type { Call1, Fn1, Fn2, PartialApplyW } from "../../HKT"; import type { ValueOf } from "../../Obj/ValueOf"; import type { Eq, IsAny, Lazied, Lazied$Get } from "../../helpers"; /** * The result of comparing two {@link Ord} instances. */ export type Ordering = GT | EQ | LT; /** * An {@link Ordering} to represent that one is _greater than_ the other. */ export type GT = 1; /** * An {@link Ordering} to represent that one is _equal to_ the other. */ export type EQ = 0; /** * An {@link Ordering} to represent that one is _less than_ the other. */ export type LT = -1; /** * A type class for types that can be compared. */ export type Ord = Lazied$Get<OrdL>; /** * {@link Lazied} version of {@link Ord}. */ export type OrdL = Lazied<OrdImpl[keyof OrdImpl][0]>; /** * Type class constraints for type class {@link Ord}. */ export interface TypeClass$$Ord<T> { Compare: Fn2<T, T, Ordering>; } /** * Implementations for type class {@link Ord}. */ export interface OrdImpl {} /** * Helper type for implementing type class {@link Ord}. * * @example * ```typescript * import type { Args, Fn2, Ord, Ordering } from "rivo"; * import type { TypeClass$$Ord } from "rivo/typeclass"; * * interface MyType<N extends number = number> { * value: N; * } * * declare module "rivo/typeclass/Ord" { * interface OrdImpl { * MyType: ImplOrdFor<MyType, MyType$$Ord>; * // ^^^^^^ * // Can be any property key * } * } * * interface MyType$$Ord extends TypeClass$$Ord<MyType> { * Compare: MyType$$Ord$Compare; * } * * interface MyType$$Ord$Compare extends Fn2<MyType, MyType, Ordering> { * def: ([o1, o2]: Args<this>) => Num.Compare<(typeof o1)["value"], (typeof o2)["value"]>; * } * * type R = Ord.Compare<MyType<1>, MyType<1>>; * // ^?: EQ * ``` */ export type ImplOrdFor<T, TypeClass extends TypeClass$$Ord<T>> = [T, TypeClass]; /** * Helper type for implementing type class {@link Ord} for generic types. * * @example * ```typescript * import type { Args, Cons, Fn1, Fn2, Lazied, Lazied$Get, Ord, OrdL, Ordering } from "rivo"; * import type { Ord$GetConstruct, TypeClass$$Ord } from "rivo/typeclass"; * import type { Compare } from "rivo/typeclass/Ord/Compare"; * // ^^^^^^^ * // The internal implementation for `Ord.Compare` that does not ensure type safety * * interface Boxed<T> { * value: T; * } * interface BoxedL<L extends Lazied> extends Boxed<Lazied$Get<L>> {} * * declare module "rivo/typeclass/Ord" { * interface OrdImpl { * Boxed: ImplOrdForGeneric<BoxedL<OrdL>, Boxed$$Ord$$Relaxer, Boxed$$Ord$$Builder>; * // ^^^^^^^^^^^^ * // Lazied version of `Boxed` instead of `Boxed<Ord>` to avoid circular reference error * } * } * * // Used to relax the type of `Ord` instance to its construct, e.g. `Boxed<42>` -> `Boxed<number>`, * // `Boxed<Boxed<42>>` -> `Boxed<Boxed<number>>`, etc. * interface Boxed$$Ord$$Relaxer extends Fn1<Boxed<Ord>, Boxed<Cons<Ord>>> { * def: ([b]: Args<this>) => Boxed<Ord$GetConstruct<(typeof b)["value"]>>; * } * * // Used to build type class instance for `Ord` instance, e.g. `Boxed<42>` -> `Boxed$$Ord<number>`, * // `Boxed<Boxed<42>>` -> `Boxed$$Ord<Boxed<number>>`, etc. * interface Boxed$$Ord$$Builder extends Fn1<Boxed<Cons<Ord>>, Boxed$$Ord<Cons<Ord>>> { * def: ([bCons]: Args<this>) => Boxed$$Ord<(typeof bCons)["value"]>; * } * * interface Boxed$$Ord<O extends Cons<Ord>> extends TypeClass$$Ord<Boxed<O>> { * Compare: Boxed$$Ord$Compare<O>; * } * interface Boxed$$Ord$Compare<O extends Cons<Ord>> extends Fn2<Boxed<O>, Boxed<O>, Ordering> { * def: ([b1, b2]: Args<this>) => Compare<(typeof b1)["value"], (typeof b2)["value"]>; * } * * type R = Ord.Compare<Boxed<42>, Boxed<42>>; * // ^?: EQ * ``` */ export type ImplOrdForGeneric< T, Relaxer extends Fn1<T, T>, TypeClassBuilder extends Fn1<T, TypeClass$$Ord<T>>, > = [T, Relaxer, TypeClassBuilder]; /** * Get the matching entry of {@link OrdImpl} for `O`. * @private */ type _Ord$GetMatch<O extends Ord> = ValueOf<{ [P in keyof OrdImpl as O extends OrdImpl[P][0] ? P : never]: OrdImpl[P]; }>; /** * Get the construct of `O` from {@link OrdImpl}. * * @example * ```typescript * type R1 = Ord$GetConstruct<42>; * // ^?: number * type R2 = Ord$GetConstruct<Some<42>>; * // ^?: Option<number> * type R3 = Ord$GetConstruct<Option<Option<42>>>; * // ^?: Option<Option<number>> * ``` */ export type Ord$GetConstruct<O extends Ord> = Eq<O, Ord> extends true ? Ord : _Ord$GetMatch<O> extends [infer T, unknown] ? T : _Ord$GetMatch<O> extends [unknown, infer Relaxer extends Fn1<any, any>, unknown] ? Call1<Relaxer, O> : never; /** * The **unsafe** version of {@link Ord$GetConstruct} (i.e. no type checking with `O`). */ export type Ord$GetConstructW<O> = O extends Ord ? Ord$GetConstruct<O> : never; /** * Get the type class of `O` from {@link OrdImpl}. * * @example * ```typescript * type R1 = Ord$GetTypeClass<42>; * // ^?: Num$$Ord * type R2 = Ord$GetTypeClass<Some<42>>; * // ^?: Option$$Ord<number> * type R3 = Ord$GetTypeClass<Option<Option<42>>>; * // ^?: Option$$Ord<Option<number>> * ``` */ export type Ord$GetTypeClass<O extends Ord> = _Ord$GetMatch<O> extends [unknown, infer TypeClass extends TypeClass$$Ord<any>] ? TypeClass : _Ord$GetMatch<O> extends ( [ unknown, infer Relaxer extends Fn1<any, unknown>, infer TypeClassBuilder extends Fn1<any, TypeClass$$Ord<any>>, ] ) ? Call1<TypeClassBuilder, Call1<Relaxer, O>> : never; /** * The **unsafe** version of {@link Ord$GetTypeClass} (i.e. no type checking with `O`). */ export type Ord$GetTypeClassW<O> = O extends Ord ? Ord$GetTypeClass<O> : never; /*********** * Methods * ***********/ /** * Methods for `Ord`. */ export namespace Ord { /** * [Fn] Compare two `Ord` instances. * * Sig: `<O extends Cons<Ord>>(o1: O, o2: O) => Ordering` * * @example * ```typescript * type R1 = $<Ord.Compare<number>, 1, 2>; * // ^?: LT * type R2 = $<Ord.Compare<number>, 2, 1>; * // ^?: GT * type R3 = $<Ord.Compare<number>, 1, 1>; * // ^?: EQ * ``` */ export type Compare<OrdCons extends Ord = Ord> = CompareFn<OrdCons>; /** * [Fn] Compare two `Ord` instances. * * Sig: `<O extends Cons<Ord>>[o2: O](o1: O) => Ordering` */ export type CompareWith<O2 extends Ord> = PartialApplyW< CompareFn< IsAny<O2> extends true ? Ord : Ord$GetConstruct<O2> extends infer Cons extends Ord ? Cons : never >, [O2], "_1" >; /** * [Fn] Compare two `Ord` instances. * * Sig: `<O extends Cons<Ord>>[o1: O](o2: O) => Ordering` */ export type CompareAgainst<O1 extends Ord> = PartialApplyW< CompareFn< IsAny<O1> extends true ? Ord : Ord$GetConstruct<O1> extends infer Cons extends Ord ? Cons : never >, [O1] >; }