UNPKG

rivo

Version:

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

229 lines (220 loc) • 7.44 kB
import type ConcatFn from "./Concat"; import type { Call1, Fn1, Fn2, PartialApplyW } from "../../HKT"; import type { ValueOf } from "../../Obj/ValueOf"; import type { Eq, IsAny, Lazied, Lazied$Get } from "../../helpers"; /** * A type class for types that can be compared. */ export type Semigroup = Lazied$Get<SemigroupL>; /** * {@link Lazied} version of {@link Semigroup}. */ export type SemigroupL = Lazied<SemigroupImpl[keyof SemigroupImpl][0]>; /** * Type class constraints for type class {@link Semigroup}. */ export interface TypeClass$$Semigroup<T> { /** * Concatenates two {@link Semigroup} instances. * * Sig: `<T>(s1: T, s2: T) => T` */ Concat: Fn2<T, T, T>; } /** * Implementations for type class {@link Semigroup}. */ export interface SemigroupImpl {} /** * Helper type for implementing type class {@link Semigroup}. * * @example * ```typescript * import type { Args, Fn, Semigroup } from "rivo"; * import type { TypeClass$$Semigroup } from "rivo/typeclass"; * * interface StringHolder<S extends string = string> { * value: S; * } * * declare module "rivo/typeclass/Semigroup" { * interface SemigroupImpl { * StringHolder: ImplSemigroupFor<StringHolder, MyType$$Semigroup>; * // ^^^^^^^^^^^^ * // Can be any property key * } * } * * interface StringHolder$$Semigroup extends TypeClass$$Semigroup<StringHolder> { * Concat: StringHolder$$Semigroup$ConcatFn; * } * * interface StringHolder$$Semigroup$ConcatFn extends Fn<[StringHolder, StringHolder], StringHolder> { * def: ([s1, s2]: Args<this>) => StringHolder<`${(typeof s1)["value"]}${(typeof s2)["value"]}`>; * } * * type R = Semigroup.Concat<StringHolder<"foo">, StringHolder<"bar">>; * // ^?: StringHolder<"foobar"> * ``` */ export type ImplSemigroupFor<T, TypeClass extends TypeClass$$Semigroup<T>> = [T, TypeClass]; /** * Helper type for implementing type class {@link Semigroup} for generic types. * * @example * ```typescript * import type { Args, Cons, Fn, Fn1, Lazied, Lazied$Get, Semigroup, SemigroupL } from "rivo"; * import type { Semigroup$GetConstruct, TypeClass$$Semigroup } from "rivo/typeclass"; * import type { Concat } from "rivo/typeclass/Semigroup/Concat"; * // ^^^^^^ * // The internal implementation for `Semigroup.Concat` 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/Semigroup" { * interface SemigroupImpl { * Boxed: ImplSemigroupForGeneric<BoxedL<SemigroupL>, Boxed$$Semigroup$$Relaxer, Boxed$$Semigroup$$Builder>; * // ^^^^^^^^^^^^^^^^^^ * // Lazied version of `Boxed` instead of `Boxed<Semigroup>` to avoid circular reference error * } * } * * // Used to relax the type of `Semigroup` instance to its construct, e.g. `Boxed<42>` -> `Boxed<number>`, * // `Boxed<Boxed<42>>` -> `Boxed<Boxed<number>>`, etc. * interface Boxed$$Semigroup$$Relaxer extends Fn1<Boxed<Semigroup>, Boxed<Cons<Semigroup>>> { * def: ([s]: Args<this>) => Boxed<Semigroup$GetConstruct<(typeof s)["value"]>>; * } * * // Used to build type class instance for `Semigroup` instance, e.g. `Boxed<42>` -> `Boxed$$Semigroup<number>`, * // `Boxed<Boxed<42>>` -> `Boxed$$Semigroup<Boxed<number>>`, etc. * interface Boxed$$Semigroup$$Builder extends Fn1<Boxed<Cons<Semigroup>>, Boxed$$Semigroup<Cons<Semigroup>>> { * def: ([sCons]: Args<this>) => Boxed$$Semigroup<(typeof sCons)["value"]>; * } * * interface Boxed$$Semigroup<S extends Cons<Semigroup>> extends TypeClass$$Semigroup<Boxed<S>> { * Concat: Boxed$$Semigroup$Concat<S>; * } * interface Boxed$$Semigroup$Concat<S extends Cons<Semigroup>> extends Fn<[Boxed<S>, Boxed<S>], Boxed<S>> { * def: ([s1, s2]: Args<this>) => Concat<(typeof s1)["value"], (typeof s2)["value"]>; * } * * type R = Semigroup.Concat<Boxed<42>, Boxed<42>>; * // ^?: 84 * ``` */ export type ImplSemigroupForGeneric< T, Relaxer extends Fn1<T, T>, TypeClassBuilder extends Fn1<T, TypeClass$$Semigroup<T>>, > = [T, Relaxer, TypeClassBuilder]; /** * Get the matching entry of {@link SemigroupImpl} for `S`. * @private */ type _Semigroup$GetMatch<S extends Semigroup> = ValueOf<{ [P in keyof SemigroupImpl as S extends SemigroupImpl[P][0] ? P : never]: SemigroupImpl[P]; }>; /** * Get the construct of `S` from {@link SemigroupImpl}. * * @example * ```typescript * type R1 = Semigroup$GetConstruct<42>; * // ^?: number * type R2 = Semigroup$GetConstruct<[42]>; * // ^?: List<number> * type R3 = Semigroup$GetConstruct<[["foo", "bar"], ["baz"]]>; * // ^?: List<List<string>> * ``` */ export type Semigroup$GetConstruct<S extends Semigroup> = Eq<S, Semigroup> extends true ? Semigroup : _Semigroup$GetMatch<S> extends [infer T, unknown] ? T : _Semigroup$GetMatch<S> extends [unknown, infer Relaxer extends Fn1<any, any>, unknown] ? Call1<Relaxer, S> : never; /** * The **unsafe** version of {@link Semigroup$GetConstruct} (i.e. no type checking with `S`). */ export type Semigroup$GetConstructW<S> = S extends Semigroup ? Semigroup$GetConstruct<S> : never; /** * Get the type class of `S` from {@link SemigroupImpl}. * * @example * ```typescript * type R1 = Semigroup$GetTypeClass<42>; * // ^?: Num$$Semigroup * type R2 = Semigroup$GetTypeClass<[42]>; * // ^?: List$$Semigroup<number> * type R3 = Semigroup$GetTypeClass<[["foo", "bar"], ["baz"]]>; * // ^?: List$$Semigroup<List<string>> * ``` */ export type Semigroup$GetTypeClass<S extends Semigroup> = _Semigroup$GetMatch<S> extends [unknown, infer TypeClass extends TypeClass$$Semigroup<any>] ? TypeClass : _Semigroup$GetMatch<S> extends ( [ unknown, infer Relaxer extends Fn1<any, unknown>, infer TypeClassBuilder extends Fn1<any, TypeClass$$Semigroup<any>>, ] ) ? Call1<TypeClassBuilder, Call1<Relaxer, S>> : never; /** * The **unsafe** version of {@link Semigroup$GetTypeClass} (i.e. no type checking with `S`). */ export type Semigroup$GetTypeClassW<S> = S extends Semigroup ? Semigroup$GetTypeClass<S> : never; /*********** * Methods * ***********/ /** * Methods for `Semigroup`. */ export namespace Semigroup { /** * [Fn] Concat two `Semigroup` instances. * * Sig: `<S extends Cons<Semigroup>>(s1: S, s2: S) => S` * * @example * ```typescript * type R1 = $<Semigroup.Concat<string>, "foo", "bar">; * // ^?: "foobar" * type R2 = $<Semigroup.Concat<List<string>>, ["foo"], ["bar", "baz"]>; * // ^?: ["foo", "bar", "baz"] * ``` */ export type Concat<SemigroupCons extends Semigroup = Semigroup> = ConcatFn<SemigroupCons>; /** * [Fn] Concat two `Semigroup` instances. * * Sig: `<S extends Cons<Semigroup>>[s2: S](s1: S) => S` */ export type Extend<S2 extends Semigroup> = PartialApplyW< ConcatFn< IsAny<S2> extends true ? Semigroup : Semigroup$GetConstruct<S2> extends infer Cons extends Semigroup ? Cons : never >, [S2], "_1" >; /** * [Fn] Concat two `Semigroup` instances. * * Sig: `<S extends Cons<Semigroup>>[s1: S](s2: S) => S` */ export type ExtendLeft<S1 extends Semigroup> = PartialApplyW< ConcatFn< IsAny<S1> extends true ? Semigroup : Semigroup$GetConstruct<S1> extends infer Cons extends Semigroup ? Cons : never >, [S1] >; }