1/**
2 * Internal and advanced utilities used by Effect's generator-based syntax and
3 * higher-kinded type support. This is not a general-purpose utility module for
4 * application code.
5 *
6 * `SingleShotGen` makes an Effect-style value work with `yield*` inside
7 * generator helpers. `Variance` and `Gen` provide the type-level signatures
8 * used by modules such as `Effect`, `Option`, and `Result` to type their
9 * `gen` APIs.
10 *
11 * @since 2.0.0
12 */
13import type { Kind, TypeLambda } from "./HKT.ts"
14import { getStackTraceLimit } from "./internal/stackTraceLimit.ts"
15import type * as Types from "./Types.ts"
16
17/**
18 * Yields its wrapped value exactly once, then completes with the value sent
19 * back in.
20 *
21 * **When to use**
22 *
23 * Use to implement `[Symbol.iterator]()` on Effect-like types so they can be
24 * `yield*`-ed inside generator functions, such as `Effect.gen` and
25 * `Option.gen`.
26 *
27 * **Details**
28 *
29 * The first call to `next()` returns a fresh `{ value: self, done: false }`.
30 * Every subsequent call returns `{ value: a, done: true }` where `a` is the
31 * argument passed to `next()`. To keep `yield*` cheap, the completion result
32 * is the iterator itself rather than a new object, so only the iterator and
33 * the single yielded result are allocated per `yield*`.
34 *
35 * **Example** (Yielding a wrapped value in a generator)
36 *
37 * ```ts import.meta.vitest
38 * import { Utils } from "effect"
39 *
40 * const gen = new Utils.SingleShotGen<string, number>("hello")
41 *
42 * gen.next(0).value // => "hello"
43 *
44 * gen.next(42).value // => 42
45 * ```
46 *
47 * @see {@link Gen} for the type-level signature that relies on `SingleShotGen`
48 * @category constructors
49 * @since 2.0.0
50 */
51export class SingleShotGen<T, A> implements Iterator<T, A> {
52 declare private value: T | A
53 declare private done: boolean
54
55 constructor(self: T) {
56 this.value = self
57 this.done = false
58 }
59
60 /**
61 * Yields the stored value once, then completes with the value sent back in.
62 *
63 * **When to use**
64 *
65 * Use to advance a `SingleShotGen` through its single yield and completion
66 * step.
67 *
68 * @since 2.0.0
69 */
70 next(a: A): IteratorResult<T, A> {
71 if (this.done) {
72 this.value = a
73 return this as unknown as IteratorReturnResult<A>
74 }
75 this.done = true
76 return { value: this.value as T, done: false }
77 }
78}
79
80/**
81 * Type-level marker encoding the variance of a `TypeLambda`'s type
82 * parameters.
83 *
84 * **When to use**
85 *
86 * Use to define variance constraints for a higher-kinded type so that
87 * {@link Gen} can correctly infer `R`, `O`, and `E` from yielded values.
88 *
89 * **Details**
90 *
91 * `F` is invariant and must match exactly. `R` is contravariant in the input
92 * or environment position. `O` and `E` are covariant in the output and error
93 * positions. This is a pure type-level construct with no runtime
94 * representation.
95 *
96 * **Example** (Declaring variance for a TypeLambda)
97 *
98 * ```ts import.meta.vitest
99 * import type { Option, Utils } from "effect"
100 *
101 * const variance: Utils.Variance<
102 * Option.OptionTypeLambda,
103 * unknown,
104 * string,
105 * string
106 * > = {
107 * _F: (value) => value,
108 * _R: () => {},
109 * _O: () => "output",
110 * _E: () => "error"
111 * }
112 * Array.of(variance._O(undefined as never), variance._E(undefined as never)) // => ["output", "error"]
113 * ```
114 *
115 * @see {@link Gen} for the type-level signature that uses `Variance`
116 * @category models
117 * @since 2.0.0
118 */
119export interface Variance<in out F extends TypeLambda, in R, out O, out E> {
120 readonly _F: Types.Invariant<F>
121 readonly _R: Types.Contravariant<R>
122 readonly _O: Types.Covariant<O>
123 readonly _E: Types.Covariant<E>
124}
125
126/**
127 * Type-level signature for generator-based monadic composition over any
128 * `TypeLambda`.
129 *
130 * **When to use**
131 *
132 * Use to type the `gen` function of a module that supports generator syntax,
133 * such as `Option.gen`, `Result.gen`, and `Effect.gen`.
134 *
135 * **Details**
136 *
137 * This is a pure type alias with no runtime behavior. It infers `R`, `O`, and
138 * `E` from the yielded values via {@link Variance} or `Kind` constraints. The
139 * generator's return type `A` becomes the output's `A` parameter.
140 *
141 * **Example** (Typing a gen function for Option)
142 *
143 * ```ts import.meta.vitest
144 * import { Option } from "effect"
145 * import type { Utils } from "effect"
146 *
147 * const gen: Utils.Gen<Option.OptionTypeLambda> = Option.gen
148 * const result = gen(function*() {
149 * return yield* Option.some(1)
150 * })
151 * result // => Option.some(1)
152 * ```
153 *
154 * @see {@link Variance} for encoding the variance used for inference
155 * @see {@link SingleShotGen} for the iterator protocol that makes yielding work
156 * @category models
157 * @since 2.0.0
158 */
159export type Gen<F extends TypeLambda> = <
160 Self,
161 K extends Variance<F, any, any, any> | Kind<F, any, any, any, any>,
162 A
163>(
164 ...args:
165 | [
166 self: Self,
167 body: (this: Self) => Generator<K, A, never>
168 ]
169 | [
170 body: () => Generator<K, A, never>
171 ]
172) => Kind<
173 F,
174 [K] extends [Variance<F, infer R, any, any>] ? R
175 : [K] extends [Kind<F, infer R, any, any, any>] ? R
176 : never,
177 [K] extends [Variance<F, any, infer O, any>] ? O
178 : [K] extends [Kind<F, any, infer O, any, any>] ? O
179 : never,
180 [K] extends [Variance<F, any, any, infer E>] ? E
181 : [K] extends [Kind<F, any, any, infer E, any>] ? E
182 : never,
183 A
184>
185
186// the probe is wrapped in a single function call (rather than module-level
187// statements) so the whole selection is pure-annotated by the build and
188// tree-shakable when `internalCall` is unused.
189const pickInternalCall = (): <A>(body: () => A) => A => {
190 const InternalTypeId = "~effect/Utils/internal"
191
192 const standard = {
193 [InternalTypeId]: <A>(body: () => A) => {
194 return body()
195 }
196 }
197
198 const forced = {
199 [InternalTypeId]: <A>(body: () => A) => {
200 try {
201 return body()
202 } finally {
203 //
204 }
205 }
206 }
207
208 const isNotOptimizedAway = getStackTraceLimit() !== 0 &&
209 standard[InternalTypeId](() => new Error().stack)?.includes(InternalTypeId) === true
210
211 return isNotOptimizedAway ? standard[InternalTypeId] : forced[InternalTypeId]
212}
213
214/** @internal */
215export const internalCall = pickInternalCall()