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