effect
Version:
The missing standard library for TypeScript, for writing production-grade software.
1,844 lines (1,705 loc) • 157 kB
text/typescript
/**
* A lightweight alternative to the `Effect` data type, with a subset of the functionality.
*
* @since 3.4.0
* @experimental
*/
import * as Arr from "./Array.js"
import type { Channel } from "./Channel.js"
import * as Context from "./Context.js"
import type { Effect, EffectUnify, EffectUnifyIgnore } from "./Effect.js"
import * as Effectable from "./Effectable.js"
import * as Either from "./Either.js"
import * as Equal from "./Equal.js"
import type { LazyArg } from "./Function.js"
import { constTrue, constVoid, dual, identity } from "./Function.js"
import { globalValue } from "./GlobalValue.js"
import * as Hash from "./Hash.js"
import type { TypeLambda } from "./HKT.js"
import type { Inspectable } from "./Inspectable.js"
import { format, NodeInspectSymbol, toStringUnknown } from "./Inspectable.js"
import * as InternalContext from "./internal/context.js"
import * as doNotation from "./internal/doNotation.js"
import { StructuralPrototype } from "./internal/effectable.js"
import * as Option from "./Option.js"
import type { Pipeable } from "./Pipeable.js"
import { pipeArguments } from "./Pipeable.js"
import type { Predicate, Refinement } from "./Predicate.js"
import { hasProperty, isIterable, isTagged } from "./Predicate.js"
import type { Sink } from "./Sink.js"
import type { Stream } from "./Stream.js"
import type { Concurrency, Covariant, Equals, NoExcessProperties, NotFunction, Simplify } from "./Types.js"
import type * as Unify from "./Unify.js"
import { SingleShotGen, YieldWrap, yieldWrapGet } from "./Utils.js"
/**
* @since 3.4.0
* @experimental
* @category type ids
*/
export const TypeId: unique symbol = Symbol.for("effect/Micro")
/**
* @since 3.4.0
* @experimental
* @category type ids
*/
export type TypeId = typeof TypeId
/**
* @since 3.4.0
* @experimental
* @category MicroExit
*/
export const MicroExitTypeId: unique symbol = Symbol.for(
"effect/Micro/MicroExit"
)
/**
* @since 3.4.0
* @experimental
* @category MicroExit
*/
export type MicroExitTypeId = typeof TypeId
/**
* A lightweight alternative to the `Effect` data type, with a subset of the functionality.
*
* @since 3.4.0
* @experimental
* @category models
*/
export interface Micro<out A, out E = never, out R = never> extends Effect<A, E, R> {
readonly [TypeId]: Micro.Variance<A, E, R>
[Symbol.iterator](): MicroIterator<Micro<A, E, R>>
[Unify.typeSymbol]?: unknown
[Unify.unifySymbol]?: MicroUnify<this>
[Unify.ignoreSymbol]?: MicroUnifyIgnore
}
/**
* @category models
* @since 3.4.3
*/
export interface MicroUnify<A extends { [Unify.typeSymbol]?: any }> extends EffectUnify<A> {
Micro?: () => A[Unify.typeSymbol] extends Micro<infer A0, infer E0, infer R0> | infer _ ? Micro<A0, E0, R0> : never
}
/**
* @category models
* @since 3.4.3
*/
export interface MicroUnifyIgnore extends EffectUnifyIgnore {
Effect?: true
}
/**
* @category type lambdas
* @since 3.4.1
*/
export interface MicroTypeLambda extends TypeLambda {
readonly type: Micro<this["Target"], this["Out1"], this["Out2"]>
}
/**
* @since 3.4.0
* @experimental
*/
export declare namespace Micro {
/**
* @since 3.4.0
* @experimental
*/
export interface Variance<A, E, R> {
_A: Covariant<A>
_E: Covariant<E>
_R: Covariant<R>
}
/**
* @since 3.4.0
* @experimental
*/
export type Success<T> = T extends Micro<infer _A, infer _E, infer _R> ? _A : never
/**
* @since 3.4.0
* @experimental
*/
export type Error<T> = T extends Micro<infer _A, infer _E, infer _R> ? _E : never
/**
* @since 3.4.0
* @experimental
*/
export type Context<T> = T extends Micro<infer _A, infer _E, infer _R> ? _R : never
}
/**
* @since 3.4.0
* @experimental
* @category guards
*/
export const isMicro = (u: unknown): u is Micro<any, any, any> => typeof u === "object" && u !== null && TypeId in u
/**
* @since 3.4.0
* @experimental
* @category models
*/
export interface MicroIterator<T extends Micro<any, any, any>> {
next(...args: ReadonlyArray<any>): IteratorResult<YieldWrap<T>, Micro.Success<T>>
}
// ----------------------------------------------------------------------------
// MicroCause
// ----------------------------------------------------------------------------
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export const MicroCauseTypeId = Symbol.for("effect/Micro/MicroCause")
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export type MicroCauseTypeId = typeof MicroCauseTypeId
/**
* A `MicroCause` is a data type that represents the different ways a `Micro` can fail.
*
* **Details**
*
* `MicroCause` comes in three forms:
*
* - `Die`: Indicates an unforeseen defect that wasn't planned for in the system's logic.
* - `Fail`: Covers anticipated errors that are recognized and typically handled within the application.
* - `Interrupt`: Signifies an operation that has been purposefully stopped.
*
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export type MicroCause<E> =
| MicroCause.Die
| MicroCause.Fail<E>
| MicroCause.Interrupt
/**
* @since 3.6.6
* @experimental
* @category guards
*/
export const isMicroCause = (self: unknown): self is MicroCause<unknown> => hasProperty(self, MicroCauseTypeId)
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export declare namespace MicroCause {
/**
* @since 3.4.6
* @experimental
*/
export type Error<T> = T extends MicroCause.Fail<infer E> ? E : never
/**
* @since 3.4.0
* @experimental
*/
export interface Proto<Tag extends string, E> extends Pipeable, globalThis.Error {
readonly [MicroCauseTypeId]: {
_E: Covariant<E>
}
readonly _tag: Tag
readonly traces: ReadonlyArray<string>
}
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export interface Die extends Proto<"Die", never> {
readonly defect: unknown
}
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export interface Fail<E> extends Proto<"Fail", E> {
readonly error: E
}
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export interface Interrupt extends Proto<"Interrupt", never> {}
}
const microCauseVariance = {
_E: identity
}
abstract class MicroCauseImpl<Tag extends string, E> extends globalThis.Error implements MicroCause.Proto<Tag, E> {
readonly [MicroCauseTypeId]: {
_E: Covariant<E>
}
constructor(
readonly _tag: Tag,
originalError: unknown,
readonly traces: ReadonlyArray<string>
) {
const causeName = `MicroCause.${_tag}`
let name: string
let message: string
let stack: string
if (originalError instanceof globalThis.Error) {
name = `(${causeName}) ${originalError.name}`
message = originalError.message as string
const messageLines = message.split("\n").length
stack = originalError.stack
? `(${causeName}) ${
originalError.stack
.split("\n")
.slice(0, messageLines + 3)
.join("\n")
}`
: `${name}: ${message}`
} else {
name = causeName
message = toStringUnknown(originalError, 0)
stack = `${name}: ${message}`
}
if (traces.length > 0) {
stack += `\n ${traces.join("\n ")}`
}
super(message)
this[MicroCauseTypeId] = microCauseVariance
this.name = name
this.stack = stack
}
pipe() {
return pipeArguments(this, arguments)
}
toString() {
return this.stack
}
[NodeInspectSymbol]() {
return this.stack
}
}
class Fail<E> extends MicroCauseImpl<"Fail", E> implements MicroCause.Fail<E> {
constructor(
readonly error: E,
traces: ReadonlyArray<string> = []
) {
super("Fail", error, traces)
}
}
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export const causeFail = <E>(
error: E,
traces: ReadonlyArray<string> = []
): MicroCause<E> => new Fail(error, traces)
class Die extends MicroCauseImpl<"Die", never> implements MicroCause.Die {
constructor(
readonly defect: unknown,
traces: ReadonlyArray<string> = []
) {
super("Die", defect, traces)
}
}
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export const causeDie = (
defect: unknown,
traces: ReadonlyArray<string> = []
): MicroCause<never> => new Die(defect, traces)
class Interrupt extends MicroCauseImpl<"Interrupt", never> implements MicroCause.Interrupt {
constructor(traces: ReadonlyArray<string> = []) {
super("Interrupt", "interrupted", traces)
}
}
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export const causeInterrupt = (
traces: ReadonlyArray<string> = []
): MicroCause<never> => new Interrupt(traces)
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export const causeIsFail = <E>(
self: MicroCause<E>
): self is MicroCause.Fail<E> => self._tag === "Fail"
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export const causeIsDie = <E>(self: MicroCause<E>): self is MicroCause.Die => self._tag === "Die"
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export const causeIsInterrupt = <E>(
self: MicroCause<E>
): self is MicroCause.Interrupt => self._tag === "Interrupt"
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export const causeSquash = <E>(self: MicroCause<E>): unknown =>
self._tag === "Fail" ? self.error : self._tag === "Die" ? self.defect : self
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
export const causeWithTrace: {
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
(trace: string): <E>(self: MicroCause<E>) => MicroCause<E>
/**
* @since 3.4.6
* @experimental
* @category MicroCause
*/
<E>(self: MicroCause<E>, trace: string): MicroCause<E>
} = dual(2, <E>(self: MicroCause<E>, trace: string): MicroCause<E> => {
const traces = [...self.traces, trace]
switch (self._tag) {
case "Die":
return causeDie(self.defect, traces)
case "Interrupt":
return causeInterrupt(traces)
case "Fail":
return causeFail(self.error, traces)
}
})
// ----------------------------------------------------------------------------
// MicroFiber
// ----------------------------------------------------------------------------
/**
* @since 3.11.0
* @experimental
* @category MicroFiber
*/
export const MicroFiberTypeId = Symbol.for("effect/Micro/MicroFiber")
/**
* @since 3.11.0
* @experimental
* @category MicroFiber
*/
export type MicroFiberTypeId = typeof MicroFiberTypeId
/**
* @since 3.11.0
* @experimental
* @category MicroFiber
*/
export interface MicroFiber<out A, out E = never> {
readonly [MicroFiberTypeId]: MicroFiber.Variance<A, E>
readonly currentOpCount: number
readonly getRef: <I, A>(ref: Context.Reference<I, A>) => A
readonly context: Context.Context<never>
readonly addObserver: (cb: (exit: MicroExit<A, E>) => void) => () => void
readonly unsafeInterrupt: () => void
readonly unsafePoll: () => MicroExit<A, E> | undefined
}
/**
* @since 3.11.0
* @experimental
* @category MicroFiber
*/
export declare namespace MicroFiber {
/**
* @since 3.11.0
* @experimental
* @category MicroFiber
*/
export interface Variance<out A, out E = never> {
readonly _A: Covariant<A>
readonly _E: Covariant<E>
}
}
const fiberVariance = {
_A: identity,
_E: identity
}
class MicroFiberImpl<in out A = any, in out E = any> implements MicroFiber<A, E> {
readonly [MicroFiberTypeId]: MicroFiber.Variance<A, E>
readonly _stack: Array<Primitive> = []
readonly _observers: Array<(exit: MicroExit<A, E>) => void> = []
_exit: MicroExit<A, E> | undefined
public _children: Set<MicroFiberImpl<any, any>> | undefined
public currentOpCount = 0
constructor(
public context: Context.Context<never>,
public interruptible = true
) {
this[MicroFiberTypeId] = fiberVariance
}
getRef<I, A>(ref: Context.Reference<I, A>): A {
return InternalContext.unsafeGetReference(this.context, ref)
}
addObserver(cb: (exit: MicroExit<A, E>) => void): () => void {
if (this._exit) {
cb(this._exit)
return constVoid
}
this._observers.push(cb)
return () => {
const index = this._observers.indexOf(cb)
if (index >= 0) {
this._observers.splice(index, 1)
}
}
}
_interrupted = false
unsafeInterrupt(): void {
if (this._exit) {
return
}
this._interrupted = true
if (this.interruptible) {
this.evaluate(exitInterrupt as any)
}
}
unsafePoll(): MicroExit<A, E> | undefined {
return this._exit
}
evaluate(effect: Primitive): void {
if (this._exit) {
return
} else if (this._yielded !== undefined) {
const yielded = this._yielded as () => void
this._yielded = undefined
yielded()
}
const exit = this.runLoop(effect)
if (exit === Yield) {
return
}
// the interruptChildren middlware is added in Micro.fork, so it can be
// tree-shaken if not used
const interruptChildren = fiberMiddleware.interruptChildren && fiberMiddleware.interruptChildren(this)
if (interruptChildren !== undefined) {
return this.evaluate(flatMap(interruptChildren, () => exit) as any)
}
this._exit = exit
for (let i = 0; i < this._observers.length; i++) {
this._observers[i](exit)
}
this._observers.length = 0
}
runLoop(effect: Primitive): MicroExit<A, E> | Yield {
let yielding = false
let current: Primitive | Yield = effect
this.currentOpCount = 0
try {
while (true) {
this.currentOpCount++
if (!yielding && this.getRef(CurrentScheduler).shouldYield(this as any)) {
yielding = true
const prev = current
current = flatMap(yieldNow, () => prev as any) as any
}
current = (current as any)[evaluate](this)
if (current === Yield) {
const yielded = this._yielded!
if (MicroExitTypeId in yielded) {
this._yielded = undefined
return yielded
}
return Yield
}
}
} catch (error) {
if (!hasProperty(current, evaluate)) {
return exitDie(`MicroFiber.runLoop: Not a valid effect: ${String(current)}`)
}
return exitDie(error)
}
}
getCont<S extends successCont | failureCont>(
symbol: S
): Primitive & Record<S, (value: any, fiber: MicroFiberImpl) => Primitive> | undefined {
while (true) {
const op = this._stack.pop()
if (!op) return undefined
const cont = op[ensureCont] && op[ensureCont](this)
if (cont) return { [symbol]: cont } as any
if (op[symbol]) return op as any
}
}
// cancel the yielded operation, or for the yielded exit value
_yielded: MicroExit<any, any> | (() => void) | undefined = undefined
yieldWith(value: MicroExit<any, any> | (() => void)): Yield {
this._yielded = value
return Yield
}
children(): Set<MicroFiber<any, any>> {
return this._children ??= new Set()
}
}
const fiberMiddleware = globalValue("effect/Micro/fiberMiddleware", () => ({
interruptChildren: undefined as ((fiber: MicroFiberImpl) => Micro<void> | undefined) | undefined
}))
const fiberInterruptChildren = (fiber: MicroFiberImpl) => {
if (fiber._children === undefined || fiber._children.size === 0) {
return undefined
}
return fiberInterruptAll(fiber._children)
}
/**
* @since 3.11.0
* @experimental
* @category MicroFiber
*/
export const fiberAwait = <A, E>(self: MicroFiber<A, E>): Micro<MicroExit<A, E>> =>
async((resume) => sync(self.addObserver((exit) => resume(succeed(exit)))))
/**
* @since 3.11.2
* @experimental
* @category MicroFiber
*/
export const fiberJoin = <A, E>(self: MicroFiber<A, E>): Micro<A, E> => flatten(fiberAwait(self))
/**
* @since 3.11.0
* @experimental
* @category MicroFiber
*/
export const fiberInterrupt = <A, E>(self: MicroFiber<A, E>): Micro<void> =>
suspend(() => {
self.unsafeInterrupt()
return asVoid(fiberAwait(self))
})
/**
* @since 3.11.0
* @experimental
* @category MicroFiber
*/
export const fiberInterruptAll = <A extends Iterable<MicroFiber<any, any>>>(fibers: A): Micro<void> =>
suspend(() => {
for (const fiber of fibers) fiber.unsafeInterrupt()
const iter = fibers[Symbol.iterator]()
const wait: Micro<void> = suspend(() => {
let result = iter.next()
while (!result.done) {
if (result.value.unsafePoll()) {
result = iter.next()
continue
}
const fiber = result.value
return async((resume) => {
fiber.addObserver((_) => {
resume(wait)
})
})
}
return exitVoid
})
return wait
})
const identifier = Symbol.for("effect/Micro/identifier")
type identifier = typeof identifier
const args = Symbol.for("effect/Micro/args")
type args = typeof args
const evaluate = Symbol.for("effect/Micro/evaluate")
type evaluate = typeof evaluate
const successCont = Symbol.for("effect/Micro/successCont")
type successCont = typeof successCont
const failureCont = Symbol.for("effect/Micro/failureCont")
type failureCont = typeof failureCont
const ensureCont = Symbol.for("effect/Micro/ensureCont")
type ensureCont = typeof ensureCont
const Yield = Symbol.for("effect/Micro/Yield")
type Yield = typeof Yield
interface Primitive {
readonly [identifier]: string
readonly [successCont]: ((value: unknown, fiber: MicroFiberImpl) => Primitive | Yield) | undefined
readonly [failureCont]:
| ((cause: MicroCause<unknown>, fiber: MicroFiberImpl) => Primitive | Yield)
| undefined
readonly [ensureCont]:
| ((fiber: MicroFiberImpl) =>
| ((value: unknown, fiber: MicroFiberImpl) => Primitive | Yield)
| undefined)
| undefined
[evaluate](fiber: MicroFiberImpl): Primitive | Yield
}
const microVariance = {
_A: identity,
_E: identity,
_R: identity
}
const MicroProto = {
...Effectable.EffectPrototype,
_op: "Micro",
[TypeId]: microVariance,
pipe() {
return pipeArguments(this, arguments)
},
[Symbol.iterator]() {
return new SingleShotGen(new YieldWrap(this)) as any
},
toJSON(this: Primitive) {
return {
_id: "Micro",
op: this[identifier],
...(args in this ? { args: this[args] } : undefined)
}
},
toString() {
return format(this)
},
[NodeInspectSymbol]() {
return format(this)
}
}
function defaultEvaluate(_fiber: MicroFiberImpl): Primitive | Yield {
return exitDie(`Micro.evaluate: Not implemented`) as any
}
const makePrimitiveProto = <Op extends string>(options: {
readonly op: Op
readonly eval?: (fiber: MicroFiberImpl) => Primitive | Micro<any, any, any> | Yield
readonly contA?: (this: Primitive, value: any, fiber: MicroFiberImpl) => Primitive | Micro<any, any, any> | Yield
readonly contE?: (
this: Primitive,
cause: MicroCause<any>,
fiber: MicroFiberImpl
) => Primitive | Micro<any, any, any> | Yield
readonly ensure?: (this: Primitive, fiber: MicroFiberImpl) => void | ((value: any, fiber: MicroFiberImpl) => void)
}): Primitive => ({
...MicroProto,
[identifier]: options.op,
[evaluate]: options.eval ?? defaultEvaluate,
[successCont]: options.contA,
[failureCont]: options.contE,
[ensureCont]: options.ensure
} as any)
const makePrimitive = <Fn extends (...args: Array<any>) => any, Single extends boolean = true>(options: {
readonly op: string
readonly single?: Single
readonly eval?: (
this: Primitive & { readonly [args]: Single extends true ? Parameters<Fn>[0] : Parameters<Fn> },
fiber: MicroFiberImpl
) => Primitive | Micro<any, any, any> | Yield
readonly contA?: (
this: Primitive & { readonly [args]: Single extends true ? Parameters<Fn>[0] : Parameters<Fn> },
value: any,
fiber: MicroFiberImpl
) => Primitive | Micro<any, any, any> | Yield
readonly contE?: (
this: Primitive & { readonly [args]: Single extends true ? Parameters<Fn>[0] : Parameters<Fn> },
cause: MicroCause<any>,
fiber: MicroFiberImpl
) => Primitive | Micro<any, any, any> | Yield
readonly ensure?: (
this: Primitive & { readonly [args]: Single extends true ? Parameters<Fn>[0] : Parameters<Fn> },
fiber: MicroFiberImpl
) => void | ((value: any, fiber: MicroFiberImpl) => void)
}): Fn => {
const Proto = makePrimitiveProto(options as any)
return function() {
const self = Object.create(Proto)
self[args] = options.single === false ? arguments : arguments[0]
return self
} as Fn
}
const makeExit = <Fn extends (...args: Array<any>) => any, Prop extends string>(options: {
readonly op: "Success" | "Failure"
readonly prop: Prop
readonly eval: (
this:
& MicroExit<unknown, unknown>
& { [args]: Parameters<Fn>[0] },
fiber: MicroFiberImpl<unknown, unknown>
) => Primitive | Yield
}): Fn => {
const Proto = {
...makePrimitiveProto(options),
[MicroExitTypeId]: MicroExitTypeId,
_tag: options.op,
get [options.prop](): any {
return (this as any)[args]
},
toJSON(this: any) {
return {
_id: "MicroExit",
_tag: options.op,
[options.prop]: this[args]
}
},
[Equal.symbol](this: any, that: any): boolean {
return isMicroExit(that) && that._tag === options.op &&
Equal.equals(this[args], (that as any)[args])
},
[Hash.symbol](this: any): number {
return Hash.cached(this, Hash.combine(Hash.string(options.op))(Hash.hash(this[args])))
}
}
return function(value: unknown) {
const self = Object.create(Proto)
self[args] = value
self[successCont] = undefined
self[failureCont] = undefined
self[ensureCont] = undefined
return self
} as Fn
}
/**
* Creates a `Micro` effect that will succeed with the specified constant value.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const succeed: <A>(value: A) => Micro<A> = makeExit({
op: "Success",
prop: "value",
eval(fiber) {
const cont = fiber.getCont(successCont)
return cont ? cont[successCont](this[args], fiber) : fiber.yieldWith(this)
}
})
/**
* Creates a `Micro` effect that will fail with the specified `MicroCause`.
*
* @since 3.4.6
* @experimental
* @category constructors
*/
export const failCause: <E>(cause: MicroCause<E>) => Micro<never, E> = makeExit({
op: "Failure",
prop: "cause",
eval(fiber) {
let cont = fiber.getCont(failureCont)
while (causeIsInterrupt(this[args]) && cont && fiber.interruptible) {
cont = fiber.getCont(failureCont)
}
return cont ? cont[failureCont](this[args], fiber) : fiber.yieldWith(this)
}
})
/**
* Creates a `Micro` effect that fails with the given error.
*
* This results in a `Fail` variant of the `MicroCause` type, where the error is
* tracked at the type level.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const fail = <E>(error: E): Micro<never, E> => failCause(causeFail(error))
/**
* Creates a `Micro` effect that succeeds with a lazily evaluated value.
*
* If the evaluation of the value throws an error, the effect will fail with a
* `Die` variant of the `MicroCause` type.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const sync: <A>(evaluate: LazyArg<A>) => Micro<A> = makePrimitive({
op: "Sync",
eval(fiber): Primitive | Yield {
const value = this[args]()
const cont = fiber.getCont(successCont)
return cont ? cont[successCont](value, fiber) : fiber.yieldWith(exitSucceed(value))
}
})
/**
* Lazily creates a `Micro` effect from the given side-effect.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const suspend: <A, E, R>(evaluate: LazyArg<Micro<A, E, R>>) => Micro<A, E, R> = makePrimitive({
op: "Suspend",
eval(_fiber) {
return this[args]()
}
})
/**
* Pause the execution of the current `Micro` effect, and resume it on the next
* scheduler tick.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const yieldNowWith: (priority?: number) => Micro<void> = makePrimitive({
op: "Yield",
eval(fiber) {
let resumed = false
fiber.getRef(CurrentScheduler).scheduleTask(() => {
if (resumed) return
fiber.evaluate(exitVoid as any)
}, this[args] ?? 0)
return fiber.yieldWith(() => {
resumed = true
})
}
})
/**
* Pause the execution of the current `Micro` effect, and resume it on the next
* scheduler tick.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const yieldNow: Micro<void> = yieldNowWith(0)
/**
* Creates a `Micro` effect that will succeed with the value wrapped in `Some`.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const succeedSome = <A>(a: A): Micro<Option.Option<A>> => succeed(Option.some(a))
/**
* Creates a `Micro` effect that succeeds with `None`.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const succeedNone: Micro<Option.Option<never>> = succeed(Option.none())
/**
* Creates a `Micro` effect that will fail with the lazily evaluated `MicroCause`.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const failCauseSync = <E>(evaluate: LazyArg<MicroCause<E>>): Micro<never, E> =>
suspend(() => failCause(evaluate()))
/**
* Creates a `Micro` effect that will die with the specified error.
*
* This results in a `Die` variant of the `MicroCause` type, where the error is
* not tracked at the type level.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const die = (defect: unknown): Micro<never> => exitDie(defect)
/**
* Creates a `Micro` effect that will fail with the lazily evaluated error.
*
* This results in a `Fail` variant of the `MicroCause` type, where the error is
* tracked at the type level.
*
* @since 3.4.6
* @experimental
* @category constructors
*/
export const failSync = <E>(error: LazyArg<E>): Micro<never, E> => suspend(() => fail(error()))
/**
* Converts an `Option` into a `Micro` effect, that will fail with
* `NoSuchElementException` if the option is `None`. Otherwise, it will succeed with the
* value of the option.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const fromOption = <A>(option: Option.Option<A>): Micro<A, NoSuchElementException> =>
option._tag === "Some" ? succeed(option.value) : fail(new NoSuchElementException({}))
/**
* Converts an `Either` into a `Micro` effect, that will fail with the left side
* of the either if it is a `Left`. Otherwise, it will succeed with the right
* side of the either.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const fromEither = <R, L>(either: Either.Either<R, L>): Micro<R, L> =>
either._tag === "Right" ? succeed(either.right) : fail(either.left)
const void_: Micro<void> = succeed(void 0)
export {
/**
* A `Micro` effect that will succeed with `void` (`undefined`).
*
* @since 3.4.0
* @experimental
* @category constructors
*/
void_ as void
}
const try_ = <A, E>(options: {
try: LazyArg<A>
catch: (error: unknown) => E
}): Micro<A, E> =>
suspend(() => {
try {
return succeed(options.try())
} catch (err) {
return fail(options.catch(err))
}
})
export {
/**
* The `Micro` equivalent of a try / catch block, which allows you to map
* thrown errors to a specific error type.
*
* @example
* ```ts
* import { Micro } from "effect"
*
* Micro.try({
* try: () => { throw new Error("boom") },
* catch: (cause) => new Error("caught", { cause })
* })
* ```
*
* @since 3.4.0
* @experimental
* @category constructors
*/
try_ as try
}
/**
* Wrap a `Promise` into a `Micro` effect.
*
* Any errors will result in a `Die` variant of the `MicroCause` type, where the
* error is not tracked at the type level.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const promise = <A>(evaluate: (signal: AbortSignal) => PromiseLike<A>): Micro<A> =>
asyncOptions<A>(function(resume, signal) {
evaluate(signal!).then(
(a) => resume(succeed(a)),
(e) => resume(die(e))
)
}, evaluate.length !== 0)
/**
* Wrap a `Promise` into a `Micro` effect. Any errors will be caught and
* converted into a specific error type.
*
* @example
* ```ts
* import { Micro } from "effect"
*
* Micro.tryPromise({
* try: () => Promise.resolve("success"),
* catch: (cause) => new Error("caught", { cause })
* })
* ```
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const tryPromise = <A, E>(options: {
readonly try: (signal: AbortSignal) => PromiseLike<A>
readonly catch: (error: unknown) => E
}): Micro<A, E> =>
asyncOptions<A, E>(function(resume, signal) {
try {
options.try(signal!).then(
(a) => resume(succeed(a)),
(e) => resume(fail(options.catch(e)))
)
} catch (err) {
resume(fail(options.catch(err)))
}
}, options.try.length !== 0)
/**
* Create a `Micro` effect using the current `MicroFiber`.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const withMicroFiber: <A, E = never, R = never>(
evaluate: (fiber: MicroFiberImpl<A, E>) => Micro<A, E, R>
) => Micro<A, E, R> = makePrimitive({
op: "WithMicroFiber",
eval(fiber) {
return this[args](fiber)
}
})
/**
* Flush any yielded effects that are waiting to be executed.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const yieldFlush: Micro<void> = withMicroFiber((fiber) => {
fiber.getRef(CurrentScheduler).flush()
return exitVoid
})
const asyncOptions: <A, E = never, R = never>(
register: (
resume: (effect: Micro<A, E, R>) => void,
signal?: AbortSignal
) => void | Micro<void, never, R>,
withSignal: boolean
) => Micro<A, E, R> = makePrimitive({
op: "Async",
single: false,
eval(fiber) {
const register = this[args][0]
let resumed = false
let yielded: boolean | Primitive = false
const controller = this[args][1] ? new AbortController() : undefined
const onCancel = register((effect) => {
if (resumed) return
resumed = true
if (yielded) {
fiber.evaluate(effect as any)
} else {
yielded = effect as any
}
}, controller?.signal)
if (yielded !== false) return yielded
yielded = true
fiber._yielded = () => {
resumed = true
}
if (controller === undefined && onCancel === undefined) {
return Yield
}
fiber._stack.push(asyncFinalizer(() => {
resumed = true
controller?.abort()
return onCancel ?? exitVoid
}))
return Yield
}
})
const asyncFinalizer: (onInterrupt: () => Micro<void, any, any>) => Primitive = makePrimitive({
op: "AsyncFinalizer",
ensure(fiber) {
if (fiber.interruptible) {
fiber.interruptible = false
fiber._stack.push(setInterruptible(true))
}
},
contE(cause, _fiber) {
return causeIsInterrupt(cause)
? flatMap(this[args](), () => failCause(cause))
: failCause(cause)
}
})
/**
* Create a `Micro` effect from an asynchronous computation.
*
* You can return a cleanup effect that will be run when the effect is aborted.
* It is also passed an `AbortSignal` that is triggered when the effect is
* aborted.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const async = <A, E = never, R = never>(
register: (
resume: (effect: Micro<A, E, R>) => void,
signal: AbortSignal
) => void | Micro<void, never, R>
): Micro<A, E, R> => asyncOptions(register as any, register.length >= 2)
/**
* A `Micro` that will never succeed or fail. It wraps `setInterval` to prevent
* the Javascript runtime from exiting.
*
* @since 3.4.0
* @experimental
* @category constructors
*/
export const never: Micro<never> = async<never>(function() {
const interval = setInterval(constVoid, 2147483646)
return sync(() => clearInterval(interval))
})
/**
* @since 3.4.0
* @experimental
* @category constructors
*/
export const gen = <Self, Eff extends YieldWrap<Micro<any, any, any>>, AEff>(
...args:
| [self: Self, body: (this: Self) => Generator<Eff, AEff, never>]
| [body: () => Generator<Eff, AEff, never>]
): Micro<
AEff,
[Eff] extends [never] ? never : [Eff] extends [YieldWrap<Micro<infer _A, infer E, infer _R>>] ? E : never,
[Eff] extends [never] ? never : [Eff] extends [YieldWrap<Micro<infer _A, infer _E, infer R>>] ? R : never
> => suspend(() => fromIterator(args.length === 1 ? args[0]() : args[1].call(args[0]) as any))
const fromIterator: (
iterator: Iterator<any, YieldWrap<Micro<any, any, any>>>
) => Micro<any, any, any> = makePrimitive({
op: "Iterator",
contA(value, fiber) {
const state = this[args].next(value)
if (state.done) return succeed(state.value)
fiber._stack.push(this)
return yieldWrapGet(state.value)
},
eval(this: any, fiber: MicroFiberImpl) {
return this[successCont](undefined, fiber)
}
})
// ----------------------------------------------------------------------------
// mapping & sequencing
// ----------------------------------------------------------------------------
/**
* Create a `Micro` effect that will replace the success value of the given
* effect.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
export const as: {
// ----------------------------------------------------------------------------
// mapping & sequencing
// ----------------------------------------------------------------------------
/**
* Create a `Micro` effect that will replace the success value of the given
* effect.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<A, B>(value: B): <E, R>(self: Micro<A, E, R>) => Micro<B, E, R>
// ----------------------------------------------------------------------------
// mapping & sequencing
// ----------------------------------------------------------------------------
/**
* Create a `Micro` effect that will replace the success value of the given
* effect.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<A, E, R, B>(self: Micro<A, E, R>, value: B): Micro<B, E, R>
} = dual(2, <A, E, R, B>(self: Micro<A, E, R>, value: B): Micro<B, E, R> => map(self, (_) => value))
/**
* Wrap the success value of this `Micro` effect in a `Some`.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
export const asSome = <A, E, R>(self: Micro<A, E, R>): Micro<Option.Option<A>, E, R> => map(self, Option.some)
/**
* Swap the error and success types of the `Micro` effect.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
export const flip = <A, E, R>(self: Micro<A, E, R>): Micro<E, A, R> =>
matchEffect(self, {
onFailure: succeed,
onSuccess: fail
})
/**
* A more flexible version of `flatMap` that combines `map` and `flatMap` into a
* single API.
*
* It also lets you directly pass a `Micro` effect, which will be executed after
* the current effect.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
export const andThen: {
/**
* A more flexible version of `flatMap` that combines `map` and `flatMap` into a
* single API.
*
* It also lets you directly pass a `Micro` effect, which will be executed after
* the current effect.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<A, X>(f: (a: A) => X): <E, R>(
self: Micro<A, E, R>
) => [X] extends [Micro<infer A1, infer E1, infer R1>] ? Micro<A1, E | E1, R | R1>
: Micro<X, E, R>
/**
* A more flexible version of `flatMap` that combines `map` and `flatMap` into a
* single API.
*
* It also lets you directly pass a `Micro` effect, which will be executed after
* the current effect.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<X>(f: NotFunction<X>): <A, E, R>(
self: Micro<A, E, R>
) => [X] extends [Micro<infer A1, infer E1, infer R1>] ? Micro<A1, E | E1, R | R1>
: Micro<X, E, R>
/**
* A more flexible version of `flatMap` that combines `map` and `flatMap` into a
* single API.
*
* It also lets you directly pass a `Micro` effect, which will be executed after
* the current effect.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<A, E, R, X>(self: Micro<A, E, R>, f: (a: A) => X): [X] extends [Micro<infer A1, infer E1, infer R1>] ? Micro<A1, E | E1, R | R1>
: Micro<X, E, R>
/**
* A more flexible version of `flatMap` that combines `map` and `flatMap` into a
* single API.
*
* It also lets you directly pass a `Micro` effect, which will be executed after
* the current effect.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<A, E, R, X>(self: Micro<A, E, R>, f: NotFunction<X>): [X] extends [Micro<infer A1, infer E1, infer R1>] ? Micro<A1, E | E1, R | R1>
: Micro<X, E, R>
} = dual(
2,
<A, E, R, B, E2, R2>(self: Micro<A, E, R>, f: any): Micro<B, E | E2, R | R2> =>
flatMap(self, (a) => {
const value = isMicro(f) ? f : typeof f === "function" ? f(a) : f
return isMicro(value) ? value : succeed(value)
})
)
/**
* Execute a side effect from the success value of the `Micro` effect.
*
* It is similar to the `andThen` api, but the success value is ignored.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
export const tap: {
/**
* Execute a side effect from the success value of the `Micro` effect.
*
* It is similar to the `andThen` api, but the success value is ignored.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<A, X>(f: (a: NoInfer<A>) => X): <E, R>(
self: Micro<A, E, R>
) => [X] extends [Micro<infer _A1, infer E1, infer R1>] ? Micro<A, E | E1, R | R1>
: Micro<A, E, R>
/**
* Execute a side effect from the success value of the `Micro` effect.
*
* It is similar to the `andThen` api, but the success value is ignored.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<X>(f: NotFunction<X>): <A, E, R>(
self: Micro<A, E, R>
) => [X] extends [Micro<infer _A1, infer E1, infer R1>] ? Micro<A, E | E1, R | R1>
: Micro<A, E, R>
/**
* Execute a side effect from the success value of the `Micro` effect.
*
* It is similar to the `andThen` api, but the success value is ignored.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<A, E, R, X>(self: Micro<A, E, R>, f: (a: NoInfer<A>) => X): [X] extends [Micro<infer _A1, infer E1, infer R1>] ? Micro<A, E | E1, R | R1>
: Micro<A, E, R>
/**
* Execute a side effect from the success value of the `Micro` effect.
*
* It is similar to the `andThen` api, but the success value is ignored.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<A, E, R, X>(self: Micro<A, E, R>, f: NotFunction<X>): [X] extends [Micro<infer _A1, infer E1, infer R1>] ? Micro<A, E | E1, R | R1>
: Micro<A, E, R>
} = dual(
2,
<A, E, R, B, E2, R2>(self: Micro<A, E, R>, f: (a: A) => Micro<B, E2, R2>): Micro<A, E | E2, R | R2> =>
flatMap(self, (a) => {
const value = isMicro(f) ? f : typeof f === "function" ? f(a) : f
return isMicro(value) ? as(value, a) : succeed(a)
})
)
/**
* Replace the success value of the `Micro` effect with `void`.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
export const asVoid = <A, E, R>(self: Micro<A, E, R>): Micro<void, E, R> => flatMap(self, (_) => exitVoid)
/**
* Access the `MicroExit` of the given `Micro` effect.
*
* @since 3.4.6
* @experimental
* @category mapping & sequencing
*/
export const exit = <A, E, R>(self: Micro<A, E, R>): Micro<MicroExit<A, E>, never, R> =>
matchCause(self, {
onFailure: exitFailCause,
onSuccess: exitSucceed
})
/**
* Replace the error type of the given `Micro` with the full `MicroCause` object.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
export const sandbox = <A, E, R>(self: Micro<A, E, R>): Micro<A, MicroCause<E>, R> => catchAllCause(self, fail)
/**
* Returns an effect that races all the specified effects,
* yielding the value of the first effect to succeed with a value. Losers of
* the race will be interrupted immediately
*
* @since 3.4.0
* @experimental
* @category sequencing
*/
export const raceAll = <Eff extends Micro<any, any, any>>(
all: Iterable<Eff>
): Micro<Micro.Success<Eff>, Micro.Error<Eff>, Micro.Context<Eff>> =>
withMicroFiber((parent) =>
async((resume) => {
const effects = Arr.fromIterable(all)
const len = effects.length
let doneCount = 0
let done = false
const fibers = new Set<MicroFiber<any, any>>()
const causes: Array<MicroCause<any>> = []
const onExit = (exit: MicroExit<any, any>) => {
doneCount++
if (exit._tag === "Failure") {
causes.push(exit.cause)
if (doneCount >= len) {
resume(failCause(causes[0]))
}
return
}
done = true
resume(fibers.size === 0 ? exit : flatMap(uninterruptible(fiberInterruptAll(fibers)), () => exit))
}
for (let i = 0; i < len; i++) {
if (done) break
const fiber = unsafeFork(parent, interruptible(effects[i]), true, true)
fibers.add(fiber)
fiber.addObserver((exit) => {
fibers.delete(fiber)
onExit(exit)
})
}
return fiberInterruptAll(fibers)
})
)
/**
* Returns an effect that races all the specified effects,
* yielding the value of the first effect to succeed or fail. Losers of
* the race will be interrupted immediately.
*
* @since 3.4.0
* @experimental
* @category sequencing
*/
export const raceAllFirst = <Eff extends Micro<any, any, any>>(
all: Iterable<Eff>
): Micro<Micro.Success<Eff>, Micro.Error<Eff>, Micro.Context<Eff>> =>
withMicroFiber((parent) =>
async((resume) => {
let done = false
const fibers = new Set<MicroFiber<any, any>>()
const onExit = (exit: MicroExit<any, any>) => {
done = true
resume(fibers.size === 0 ? exit : flatMap(fiberInterruptAll(fibers), () => exit))
}
for (const effect of all) {
if (done) break
const fiber = unsafeFork(parent, interruptible(effect), true, true)
fibers.add(fiber)
fiber.addObserver((exit) => {
fibers.delete(fiber)
onExit(exit)
})
}
return fiberInterruptAll(fibers)
})
)
/**
* Returns an effect that races two effects, yielding the value of the first
* effect to succeed. Losers of the race will be interrupted immediately.
*
* @since 3.4.0
* @experimental
* @category sequencing
*/
export const race: {
/**
* Returns an effect that races two effects, yielding the value of the first
* effect to succeed. Losers of the race will be interrupted immediately.
*
* @since 3.4.0
* @experimental
* @category sequencing
*/
<A2, E2, R2>(that: Micro<A2, E2, R2>): <A, E, R>(self: Micro<A, E, R>) => Micro<A | A2, E | E2, R | R2>
/**
* Returns an effect that races two effects, yielding the value of the first
* effect to succeed. Losers of the race will be interrupted immediately.
*
* @since 3.4.0
* @experimental
* @category sequencing
*/
<A, E, R, A2, E2, R2>(self: Micro<A, E, R>, that: Micro<A2, E2, R2>): Micro<A | A2, E | E2, R | R2>
} = dual(
2,
<A, E, R, A2, E2, R2>(self: Micro<A, E, R>, that: Micro<A2, E2, R2>): Micro<A | A2, E | E2, R | R2> =>
raceAll([self, that])
)
/**
* Returns an effect that races two effects, yielding the value of the first
* effect to succeed *or* fail. Losers of the race will be interrupted immediately.
*
* @since 3.4.0
* @experimental
* @category sequencing
*/
export const raceFirst: {
/**
* Returns an effect that races two effects, yielding the value of the first
* effect to succeed *or* fail. Losers of the race will be interrupted immediately.
*
* @since 3.4.0
* @experimental
* @category sequencing
*/
<A2, E2, R2>(that: Micro<A2, E2, R2>): <A, E, R>(self: Micro<A, E, R>) => Micro<A | A2, E | E2, R | R2>
/**
* Returns an effect that races two effects, yielding the value of the first
* effect to succeed *or* fail. Losers of the race will be interrupted immediately.
*
* @since 3.4.0
* @experimental
* @category sequencing
*/
<A, E, R, A2, E2, R2>(self: Micro<A, E, R>, that: Micro<A2, E2, R2>): Micro<A | A2, E | E2, R | R2>
} = dual(
2,
<A, E, R, A2, E2, R2>(self: Micro<A, E, R>, that: Micro<A2, E2, R2>): Micro<A | A2, E | E2, R | R2> =>
raceAllFirst([self, that])
)
/**
* Map the success value of this `Micro` effect to another `Micro` effect, then
* flatten the result.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
export const flatMap: {
/**
* Map the success value of this `Micro` effect to another `Micro` effect, then
* flatten the result.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<A, B, E2, R2>(f: (a: A) => Micro<B, E2, R2>): <E, R>(self: Micro<A, E, R>) => Micro<B, E | E2, R | R2>
/**
* Map the success value of this `Micro` effect to another `Micro` effect, then
* flatten the result.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<A, E, R, B, E2, R2>(self: Micro<A, E, R>, f: (a: A) => Micro<B, E2, R2>): Micro<B, E | E2, R | R2>
} = dual(
2,
<A, E, R, B, E2, R2>(
self: Micro<A, E, R>,
f: (a: A) => Micro<B, E2, R2>
): Micro<B, E | E2, R | R2> => {
const onSuccess = Object.create(OnSuccessProto)
onSuccess[args] = self
onSuccess[successCont] = f
return onSuccess
}
)
const OnSuccessProto = makePrimitiveProto({
op: "OnSuccess",
eval(this: any, fiber: MicroFiberImpl): Primitive {
fiber._stack.push(this)
return this[args]
}
})
// ----------------------------------------------------------------------------
// mapping & sequencing
// ----------------------------------------------------------------------------
/**
* Flattens any nested `Micro` effects, merging the error and requirement types.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
export const flatten = <A, E, R, E2, R2>(
self: Micro<Micro<A, E, R>, E2, R2>
): Micro<A, E | E2, R | R2> => flatMap(self, identity)
/**
* Transforms the success value of the `Micro` effect with the specified
* function.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
export const map: {
/**
* Transforms the success value of the `Micro` effect with the specified
* function.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<A, B>(f: (a: A) => B): <E, R>(self: Micro<A, E, R>) => Micro<B, E, R>
/**
* Transforms the success value of the `Micro` effect with the specified
* function.
*
* @since 3.4.0
* @experimental
* @category mapping & sequencing
*/
<A, E, R, B>(self: Micro<A, E, R>, f: (a: A) => B): Micro<B, E, R>
} = dual(
2,
<A, E, R, B>(self: Micro<A, E, R>, f: (a: A) => B): Micro<B, E, R> => flatMap(self, (a) => succeed(f(a)))
)
// ----------------------------------------------------------------------------
// MicroExit
// ----------------------------------------------------------------------------
/**
* The `MicroExit` type is used to represent the result of a `Micro` computation. It
* can either be successful, containing a value of type `A`, or it can fail,
* containing an error of type `E` wrapped in a `MicroCause`.
*
* @since 3.4.6
* @experimental
* @category MicroExit
*/
export type MicroExit<A, E = never> =
| MicroExit.Success<A, E>
| MicroExit.Failure<A, E>
/**
* @since 3.4.6
* @experimental
* @category MicroExit
*/
export declare namespace MicroExit {
/**
* @since 3.4.6
* @experimental
* @category MicroExit
*/
export interface Proto<out A, out E = never> extends Micro<A, E> {
readonly [MicroExitTypeId]: MicroExitTypeId
}
/**
* @since 3.4.6
* @experimental
* @category MicroExit
*/
export interface Success<out A, out E> extends Proto<A, E> {
readonly _tag: "Success"
readonly value: A
}
/**
* @since 3.4.6
* @experimental
* @category MicroExit
*/
export interface Failure<out A, out E> extends Proto<A, E> {
readonly _tag: "Failure"
readonly cause: MicroCause<E>
}
}
/**
* @since 3.4.6
* @experimental
* @category MicroExit
*/
export const isMicroExit = (u: unknown): u is MicroExit<unknown, unknown> => hasProperty(u, MicroExitTypeId)
/**
* @since 3.4.6
* @experimental
* @category MicroExit
*/
export const exitSucceed: <A>(a: A) => MicroExit<A, never> = succeed as any
/**
* @since 3.4.6
* @experimental
* @category MicroExit
*/
export const exitFailCause: <E>(cause: MicroCause<E>) => MicroExit<never, E> = failCause as any
/**
* @since 3.4.6
* @experimental
* @category MicroExit
*/
export const exitInterrupt: MicroExit<never> = exitFailCause(causeInterrupt())
/**
* @since 3.4.6
* @experimental
* @category MicroExit
*/
export const exitFail = <E>(e: E): MicroExit<never, E> => exitFailCause(causeFail(e))
/**
* @since 3.4.6
* @experimental
* @category MicroExit
*/
export const exitDie = (defect: unknown): MicroExit<never> => exitFailCause(causeDie(defect))
/**
* @since 3.4.6
* @experimental
* @category MicroExit
*/
export const exitIsSuccess = <A, E>(
self: MicroExit<A, E>
): self is MicroExit.Success<A, E> => self._tag === "Success"
/**
* @since 3.4.6
* @experimental
*