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