rivo
Version:
🤖 The ultimate library you need for composable type-level programming in TypeScript, powered by HKT.
242 lines (232 loc) • 6.99 kB
TypeScript
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]
>;
}