1/**
2 * Provides type-level helpers for generic code over container-like types.
3 *
4 * TypeScript cannot directly abstract over shapes such as `Option<A>`,
5 * `ReadonlyArray<A>`, or `Effect<A, E, R>`. This module represents those shapes
6 * with `TypeLambda` and applies concrete type arguments with `Kind`. It is
7 * mostly useful when defining generic helpers or type classes that should work
8 * across several data types.
9 *
10 * @since 2.0.0
11 */
12import type * as Types from "./Types.ts"
13
14/**
15 * Defines the unique symbol used to associate `TypeClass` implementations with their `TypeLambda`.
16 *
17 * **When to use**
18 *
19 * Use when you need to define a custom type class that exposes the `TypeLambda`
20 * it operates on.
21 *
22 * **Details**
23 *
24 * This symbol links a type class shape with its compile-time type lambda. It is
25 * intended for type-class definitions and has no runtime behavior.
26 *
27 * **Example** (Linking a type class to a type lambda)
28 *
29 * ```ts import.meta.vitest
30 * import type { HKT } from "effect"
31 *
32 * interface IdentityTypeLambda extends HKT.TypeLambda {
33 * readonly type: this["Target"]
34 * }
35 *
36 * interface IdentityTypeClass extends HKT.TypeClass<IdentityTypeLambda> {
37 * readonly [HKT.URI]?: IdentityTypeLambda
38 * readonly of: <A>(value: A) => HKT.Kind<IdentityTypeLambda, never, never, never, A>
39 * }
40 *
41 * const identity: IdentityTypeClass = {
42 * of: (value) => value
43 * }
44 *
45 * type LinkedTypeLambda = typeof identity[typeof HKT.URI]
46 *
47 * const value: HKT.Kind<NonNullable<LinkedTypeLambda>, never, never, never, string> = identity.of("ok")
48 * ```
49 *
50 * @category symbols
51 * @since 2.0.0
52 */
53export declare const URI: unique symbol
54
55/**
56 * Base interface for type classes that work with Higher-Kinded Types.
57 *
58 * **When to use**
59 *
60 * Use to define type class interfaces parameterized by a `TypeLambda`.
61 *
62 * **Details**
63 *
64 * A `TypeClass` defines operations that can be performed on any type constructor
65 * that matches the given `TypeLambda`. This enables writing generic code that
66 * works across different container types like Array, Option, Effect, etc.
67 *
68 * **Example** (Defining higher-kinded type classes)
69 *
70 * ```ts import.meta.vitest
71 * import type { HKT } from "effect"
72 *
73 * // Define a Functor type class
74 * interface Functor<F extends HKT.TypeLambda> extends HKT.TypeClass<F> {
75 * map<A, B>(
76 * fa: HKT.Kind<F, never, never, never, A>,
77 * f: (a: A) => B
78 * ): HKT.Kind<F, never, never, never, B>
79 * }
80 *
81 * // Define a Monad type class
82 * interface Monad<F extends HKT.TypeLambda> extends Functor<F> {
83 * flatMap<A, B>(
84 * fa: HKT.Kind<F, never, never, never, A>,
85 * f: (a: A) => HKT.Kind<F, never, never, never, B>
86 * ): HKT.Kind<F, never, never, never, B>
87 * }
88 *
89 * const witness: keyof Monad<HKT.TypeLambda> = "flatMap"
90 * ```
91 *
92 * @category models
93 * @since 2.0.0
94 */
95export interface TypeClass<F extends TypeLambda> {
96 readonly [URI]?: F
97}
98
99/**
100 * Base interface for defining Higher-Kinded Type parameters.
101 *
102 * **When to use**
103 *
104 * Use to encode a type constructor for higher-kinded generic programming.
105 *
106 * **Details**
107 *
108 * A `TypeLambda` encodes the "shape" of a type constructor, specifying how many
109 * type parameters it takes and their variance (contravariant, covariant, or
110 * invariant). The four parameters are `In` for contravariant input, `Out2` for
111 * covariant output often used for errors, `Out1` for covariant output often used
112 * for context or environment, and `Target` for the invariant main type.
113 *
114 * **Example** (Defining type lambdas)
115 *
116 * ```ts import.meta.vitest
117 * import type { Effect, HKT } from "effect"
118 *
119 * // TypeLambda for Array<A>
120 * interface ArrayTypeLambda extends HKT.TypeLambda {
121 * readonly type: Array<this["Target"]>
122 * }
123 *
124 * // TypeLambda for Effect<A, E, R>
125 * interface EffectTypeLambda extends HKT.TypeLambda {
126 * readonly type: Effect.Effect<this["Target"], this["Out2"], this["Out1"]>
127 * }
128 *
129 * // TypeLambda for function (A) => B
130 * interface FunctionTypeLambda extends HKT.TypeLambda {
131 * readonly type: (a: this["In"]) => this["Target"]
132 * }
133 *
134 * const witness: HKT.Kind<ArrayTypeLambda, never, never, never, string> = ["ok"]
135 * ```
136 *
137 * @category models
138 * @since 2.0.0
139 */
140export interface TypeLambda {
141 readonly In: unknown
142 readonly Out2: unknown
143 readonly Out1: unknown
144 readonly Target: unknown
145}
146
147/**
148 * Applies type parameters to a `TypeLambda` to get the concrete type.
149 *
150 * **When to use**
151 *
152 * Use to apply a `TypeLambda` to type parameters and obtain its concrete type.
153 *
154 * **Details**
155 *
156 * This type-level function takes a `TypeLambda` and four type parameters, then
157 * "applies" them to get the actual type. It handles variance correctly, ensuring
158 * contravariant parameters are used as inputs and covariant parameters as
159 * outputs. This is the core mechanism that allows HKT to transform abstract type
160 * constructors into concrete types by applying arguments.
161 *
162 * **Example** (Applying type lambdas)
163 *
164 * ```ts import.meta.vitest
165 * import { Option } from "effect"
166 * import type { Effect, HKT } from "effect"
167 *
168 * // Define TypeLambdas
169 * interface OptionTypeLambda extends HKT.TypeLambda {
170 * readonly type: Option.Option<this["Target"]>
171 * }
172 *
173 * interface EffectTypeLambda extends HKT.TypeLambda {
174 * readonly type: Effect.Effect<this["Target"], this["Out2"], this["Out1"]>
175 * }
176 *
177 * // Apply type parameters to get concrete types
178 * type OptionString = HKT.Kind<OptionTypeLambda, never, never, never, string>
179 * // Result: Option.Option<string>
180 *
181 * type EffectStringNumberBoolean = HKT.Kind<
182 * EffectTypeLambda,
183 * never,
184 * number,
185 * boolean,
186 * string
187 * >
188 * // Result: Effect.Effect<string, number, boolean>
189 *
190 * // TypeLambdas enable generic programming over type constructors
191 * type StringType<F extends HKT.TypeLambda> = HKT.Kind<
192 * F,
193 * never,
194 * never,
195 * never,
196 * string
197 * >
198 *
199 * const witness: OptionString = Option.some("ok")
200 * ```
201 *
202 * @category utility types
203 * @since 2.0.0
204 */
205export type Kind<F extends TypeLambda, In, Out2, Out1, Target> = F extends {
206 readonly type: unknown
207} ? (F & {
208 readonly In: In
209 readonly Out2: Out2
210 readonly Out1: Out1
211 readonly Target: Target
212 })["type"]
213 : {
214 readonly F: F
215 readonly In: Types.Contravariant<In>
216 readonly Out2: Types.Covariant<Out2>
217 readonly Out1: Types.Covariant<Out1>
218 readonly Target: Types.Invariant<Target>
219 }