(spec: SpawnSpec | ReadonlyArray<SpawnSpec>): Effect.Effect<
void,
| ReadyTimedOut
| ChildExited
| HandleSpent
| HandleNotReady
| AssumeTokenMismatch
| AssumeTokenReused
| AssumeNotReady
| NodeUnreachable
| ProtocolUnanswered
| ServiceNotReady
| ServiceNotServed
| UnaddressedNode
| PlatformError
| ConfigError,
ChildProcessSpawner.ChildProcessSpawner | Scope.Scope
>One-shot bring-up: spawn → awaitReady → handoff per unit, then the launcher may exit.
Accepts one SpawnSpec or a readonly array (not Group). Units run
sequentially (Effect.forEach concurrency 1) so custody stays ordered.
export const const up: (
spec: SpawnSpec | ReadonlyArray<SpawnSpec>
) => Effect.Effect<
void,
| ReadyTimedOut
| ChildExited
| HandleSpent
| HandleNotReady
| AssumeTokenMismatch
| AssumeTokenReused
| AssumeNotReady
| NodeUnreachable
| ProtocolUnanswered
| ServiceNotReady
| ServiceNotServed
| UnaddressedNode
| PlatformError
| ConfigError,
| ChildProcessSpawner.ChildProcessSpawner
| Scope.Scope
>
One-shot bring-up: spawn → awaitReady → handoff per unit, then the launcher may exit.
Accepts one
SpawnSpec
or a readonly array (not
Group
). Units run
sequentially (Effect.forEach concurrency 1) so custody stays ordered.
up = (
spec: SpawnSpec | ReadonlyArray<SpawnSpec>spec: SpawnSpec | interface ReadonlyArray<T>ReadonlyArray<SpawnSpec>,
): import EffectEffect.interface Effect<out A, out E = never, out R = never>The Effect interface defines a value that lazily describes a workflow or
job. The workflow requires some context R, and may fail with an error of
type E, or succeed with a value of type A.
When to use
Use when you need to represent a lazy, composable workflow that can require
services, fail with a typed error, or succeed with a typed value.
Details
Effect values model resourceful interaction with the outside world,
including synchronous, asynchronous, concurrent, and parallel interaction.
They use a fiber-based concurrency model, with built-in support for
scheduling, fine-grained interruption, structured concurrency, and high
scalability.
To run an Effect value, you need a Runtime, which is a type that is
capable of executing Effect values.
Effect<
void,
| class ReadyTimedOutclass ReadyTimedOut {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
services: ReadonlyArray<string> | undefined;
timeout: Duration.Input;
}
Ready poll expired before the child reported Ready.
ReadyTimedOut
| class ChildExitedclass ChildExited {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
code: number | undefined;
}
Child OS process exited while the launcher was waiting for Ready.
ChildExited
| class HandleSpentclass HandleSpent {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
phase: 'awaitReady' | 'handoff';
}
Custody handle used after handoff — .awaitReady / .handoff must not be called again.
HandleSpent
| class HandleNotReadyclass HandleNotReady {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
}
Handle.handoff
called before
Handle.awaitReady
succeeded.
HandleNotReady
| class AssumeTokenMismatchclass AssumeTokenMismatch {
message: string;
_tag: 'AssumeTokenMismatch';
node: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
Node.assume was called with a token that does not match the node's expected token
(or the node has no assume token configured).
AssumeTokenMismatch
| class AssumeTokenReusedclass AssumeTokenReused {
message: string;
_tag: 'AssumeTokenReused';
node: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
Node.assume succeeded once already — tokens are single-use for Track A handoff.
AssumeTokenReused
| class AssumeNotReadyclass AssumeNotReady {
message: string;
node: string;
_tag: 'AssumeNotReady';
serviceKey: string | undefined;
detail: string | undefined;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
Node.assume rejected because the node is not Ready yet (served HyperServices not all
ready, or a configured subset is not ready). Ready ≠ ownership — wait for Ready, then assume.
AssumeNotReady
| class NodeUnreachableclass NodeUnreachable {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
url: string;
}
A remote
Node
that didn't answer at its declared address — down, wrong port/url, or (for a
socket node) a server not speaking the socket protocol. Surfaced eagerly by
verifyConnection
so a client fails fast at startup instead of hanging or erroring opaquely at the first call.
NodeUnreachable
| class ProtocolUnansweredclass ProtocolUnanswered {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
url: string;
kind: ProtocolKind;
}
Transport answered, but the Effect RPC protocol did not — typically something else is listening
on the address (wrong process / wrong protocol). Surfaced by
Hyperlink.verifyConnection
(node, { deep: true }) after the cheap reachability probe
succeeds.
ProtocolUnanswered
| class ServiceNotReadyclass ServiceNotReady {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
url: string;
serviceKey: string;
detail: string | undefined;
}
The peer serves the target resource, but reports it not ready (ready: false).
Surfaced by
Hyperlink.verifyConnection
(node, { deep: true, resource }).
ServiceNotReady
| class ServiceNotServedclass ServiceNotServed {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
url: string;
serviceKey: string;
served: ReadonlyArray<string>;
}
The peer answered the node-status RPC, but the target resource key is not in status.services.
Surfaced by
Hyperlink.verifyConnection
(node, { deep: true, resource }).
ServiceNotServed
| class UnaddressedNodeclass UnaddressedNode {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
}
Deriving a transport from a node that never declared one — a bare Node.Tag()("x") has no
address/kind, so connect / listen can't know how to reach it. Surfaces on the Layer / Effect
error channel (never a sync throw).
UnaddressedNode
| class PlatformErrorclass PlatformError {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
reason: BadArgument | SystemError;
}
Tagged error used by platform APIs to report either invalid arguments or
system-level failures.
When to use
Use as the shared error type for platform APIs that expose invalid arguments
and host or operating-system failures through a single Effect error
channel.
Details
The reason field contains the underlying BadArgument or SystemError.
When that reason has a cause, the cause is preserved on the wrapper.
PlatformError
| class ConfigErrorclass ConfigError {
_tag: 'ConfigError';
name: string;
cause: SourceError | Schema.SchemaError;
message: string;
toString: () => string;
}
Represents the error type produced when config loading or validation fails.
When to use
Use when you need to inspect config loading or validation failures.
Details
Wraps either:
- A
SourceError — the provider could not read data (I/O failure).
- A
SchemaError — the data was found but did not match the schema
(wrong type, out of range, missing key, etc.).
ConfigError,
import ChildProcessSpawnerChildProcessSpawner.class ChildProcessSpawnerclass ChildProcessSpawner {
key: Identifier;
Service: {
spawn: (command: Command) => Effect.Effect<ChildProcessHandle, PlatformError.PlatformError, Scope.Scope>;
exitCode: (command: Command) => Effect.Effect<ExitCode, PlatformError.PlatformError>;
streamString: (command: Command, options?: { readonly includeStderr?: boolean | undefined }) => Stream.Stream<string, PlatformError.PlatformError>;
streamLines: (command: Command, options?: { readonly includeStderr?: boolean | undefined }) => Stream.Stream<string, PlatformError.PlatformError>;
lines: (command: Command, options?: { readonly includeStderr?: boolean | undefined }) => Effect.Effect<Array<string>, PlatformError.PlatformError>;
string: (command: Command, options?: { readonly includeStderr?: boolean | undefined }) => Effect.Effect<string, PlatformError.PlatformError>;
};
}
Service tag for child process spawning.
ChildProcessSpawner | import ScopeScope.Scope
> =>
const withLauncherPhase: <A, E, R>(
nodeKey: string,
phase: string,
effect: Effect.Effect<A, E, R>
) => Effect.Effect<A, E, R>
withLauncherPhase(
"up",
"up",
import EffectEffect.const gen: {
<Eff extends Effect<any, any, any>, AEff>(
f: () => Generator<Eff, AEff, never>
): Effect<
AEff,
[Eff] extends [never]
? never
: [Eff] extends [
Effect<infer _A, infer E, infer _R>
]
? E
: never,
[Eff] extends [never]
? never
: [Eff] extends [
Effect<infer _A, infer _E, infer R>
]
? R
: never
>
<Self, Eff extends Effect<any, any, any>, AEff>(
options: { readonly self: Self },
f: (this: Self) => Generator<Eff, AEff, never>
): Effect<
AEff,
[Eff] extends [never]
? never
: [Eff] extends [
Effect<infer _A, infer E, infer _R>
]
? E
: never,
[Eff] extends [never]
? never
: [Eff] extends [
Effect<infer _A, infer _E, infer R>
]
? R
: never
>
}
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to use
Use when you want to write effectful code that looks and behaves like
synchronous code, while still handling asynchronous tasks, errors, and complex
control flow such as loops and conditions.
Generator functions work similarly to async/await but keep errors,
requirements, and interruption in the Effect type. You can yield* values
from effects and return the final result at the end.
Example (Sequencing effects with generators)
import { Data, Effect } from "effect"
class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, DiscountRateError> =>
discountRate === 0
? Effect.fail(new DiscountRateError())
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function*() {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})
gen(function* () {
const const units: ReadonlyArray<SpawnSpec>units = const isSpawnSpec: (
value: unknown
) => value is SpawnSpec
isSpawnSpec(spec: SpawnSpec | ReadonlyArray<SpawnSpec>spec) ? [spec: SpawnSpec | ReadonlyArray<SpawnSpec>(parameter) spec: {
node: AnyNode;
process: ChildProcess.Command | ((token: string) => ChildProcess.Command);
ready: ReadyOptions;
}
spec] : spec: SpawnSpec | ReadonlyArray<SpawnSpec>spec;
yield* import EffectEffect.const logInfo: (
...message: ReadonlyArray<any>
) => Effect<void>
Logs one or more messages at the INFO level.
Example (Logging information)
import { Effect } from "effect"
const program = Effect.gen(function*() {
yield* Effect.logInfo("Application starting up")
yield* Effect.logInfo("Config loaded:", "production", "Port:", 3000)
// Useful for general information
const version = "1.2.3"
yield* Effect.logInfo("Application version:", version)
})
Effect.runPromise(program)
// Output:
// timestamp=2023-... level=INFO message="Application starting up"
// timestamp=2023-... level=INFO message="Config loaded: production Port: 3000"
// timestamp=2023-... level=INFO message="Application version: 1.2.3"
logInfo("Launcher.up starting").Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<void, never, never>>(this: Effect.Effect<void, never, never>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>): Effect.Effect<void, never, never> (+21 overloads)pipe(
import EffectEffect.const annotateLogs: (values: Record<string, unknown>) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+3 overloads)annotateLogs({ "launcher.units": var String: StringConstructor
;(value?: any) => string
Allows manipulation and formatting of text strings and determination and location of substrings within strings.
String(const units: ReadonlyArray<SpawnSpec>units.ReadonlyArray<SpawnSpec>.length: numberGets the length of the array. This is a number one higher than the highest element defined in an array.
length) }),
);
yield* import EffectEffect.const forEach: {
<
B,
E,
R,
S extends Iterable<any>,
Discard extends boolean = false
>(
f: (
a: Arr.ReadonlyArray.Infer<S>,
i: number
) => Effect<B, E, R>,
options?:
| {
readonly concurrency?:
| Concurrency
| undefined
readonly discard?: Discard | undefined
}
| undefined
): (
self: S
) => Effect<
Discard extends false
? Arr.ReadonlyArray.With<S, B>
: void,
E,
R
>
<
B,
E,
R,
S extends Iterable<any>,
Discard extends boolean = false
>(
self: S,
f: (
a: Arr.ReadonlyArray.Infer<S>,
i: number
) => Effect<B, E, R>,
options?:
| {
readonly concurrency?:
| Concurrency
| undefined
readonly discard?: Discard | undefined
}
| undefined
): Effect<
Discard extends false
? Arr.ReadonlyArray.With<S, B>
: void,
E,
R
>
}
Executes an effectful operation for each element in an Iterable.
When to use
Use to traverse an iterable with an effectful function while preserving
element order in the collected results.
Details
The forEach function applies a provided operation to each element in the
iterable, producing a new effect that returns an array of results.
If any effect fails, the iteration stops immediately (short-circuiting), and
the error is propagated.
Concurrency:
The concurrency option controls how many operations are performed
concurrently. By default, the operations are performed sequentially.
Discarding Results:
If the discard option is set to true, the intermediate results are not
collected, and the final result of the operation is void.
Example (Mapping over an iterable with effects)
import { Console, Effect } from "effect"
const result = Effect.forEach(
[1, 2, 3, 4, 5],
(n, index) =>
Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2))
)
Effect.runPromise(result).then(console.log)
// Output:
// Currently at index 0
// Currently at index 1
// Currently at index 2
// Currently at index 3
// Currently at index 4
// [ 2, 4, 6, 8, 10 ]
Example (Running effects without collecting results)
import { Console, Effect } from "effect"
// Apply effects but discard the results
const result = Effect.forEach(
[1, 2, 3, 4, 5],
(n, index) =>
Console.log(`Currently at index ${index}`).pipe(Effect.as(n * 2)),
{ discard: true }
)
Effect.runPromise(result).then(console.log)
// Output:
// Currently at index 0
// Currently at index 1
// Currently at index 2
// Currently at index 3
// Currently at index 4
// undefined
forEach(
const units: ReadonlyArray<SpawnSpec>units,
(unit: SpawnSpec(parameter) unit: {
node: AnyNode;
process: ChildProcess.Command | ((token: string) => ChildProcess.Command);
ready: ReadyOptions;
}
unit) =>
const spawn: (
spec: SpawnSpec
) => Effect.Effect<
Handle,
PlatformError,
| ChildProcessSpawner.ChildProcessSpawner
| Scope.Scope
>
Spawn one OS child under launcher custody — mints an assume token, runs process, returns
a
Handle
. Requires ChildProcessSpawner + Scope (provide @effect/platform-node
layers at the app edge).
spawn(unit: SpawnSpec(parameter) unit: {
node: AnyNode;
process: ChildProcess.Command | ((token: string) => ChildProcess.Command);
ready: ReadyOptions;
}
unit).Pipeable.pipe<Effect.Effect<Handle, PlatformError, ChildProcessSpawner.ChildProcessSpawner | Scope.Scope>, Effect.Effect<Handle, ReadyTimedOut | ChildExited | HandleSpent | NodeUnreachable | ProtocolUnanswered | ServiceNotReady | ServiceNotServed | UnaddressedNode | PlatformError | Config.ConfigError, ChildProcessSpawner.ChildProcessSpawner | Scope.Scope>, Effect.Effect<...>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<...>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Effect.Effect<...>): Effect.Effect<...> (+21 overloads)pipe(
import EffectEffect.const flatMap: {
<A, B, E1, R1>(
f: (a: A) => Effect<B, E1, R1>
): <E, R>(
self: Effect<A, E, R>
) => Effect<B, E1 | E, R1 | R>
<A, E, R, B, E1, R1>(
self: Effect<A, E, R>,
f: (a: A) => Effect<B, E1, R1>
): Effect<B, E | E1, R | R1>
}
Chains effects to produce new Effect instances, useful for combining
operations that depend on previous results.
When to use
Use when you need to chain multiple effects, ensuring that each
step produces a new Effect while flattening any nested effects that may
occur.
Details
flatMap lets you sequence effects so that the result of one effect can be
used in the next step. It is similar to flatMap used with arrays but works
specifically with Effect instances, allowing you to avoid deeply nested
effect structures.
Since effects are immutable, flatMap always returns a new effect instead of
changing the original one.
Example (Choosing flatMap syntax variants)
import { Effect, pipe } from "effect"
const myEffect = Effect.succeed(1)
const transformation = (n: number) => Effect.succeed(n + 1)
const flatMappedWithPipe = pipe(myEffect, Effect.flatMap(transformation))
const flatMappedWithDataFirst = Effect.flatMap(myEffect, transformation)
const flatMappedWithMethod = myEffect.pipe(Effect.flatMap(transformation))
Example (Sequencing dependent effects)
import { Data, Effect, pipe } from "effect"
class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, DiscountRateError> =>
discountRate === 0
? Effect.fail(new DiscountRateError())
: Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Chaining the fetch and discount application using `flatMap`
const finalAmount = pipe(
fetchTransactionAmount,
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(finalAmount).then(console.log)
// Output: 95
flatMap((handle: Handle(parameter) handle: {
token: Redacted.Redacted<string>;
node: AnyNode;
awaitReady: () => Effect.Effect<Handle, ReadyTimedOut | ChildExited | HandleSpent | UnaddressedNode | NodeUnreachable | ProtocolUnanswered | ServiceNotReady | ServiceNotServed | PlatformError | ConfigError>;
handoff: () => Effect.Effect<void, HandleNotReady | AssumeTokenMismatch | AssumeTokenReused | AssumeNotReady | NodeUnreachable | UnaddressedNode | HandleSpent | PlatformError>;
}
handle) => handle: Handle(parameter) handle: {
token: Redacted.Redacted<string>;
node: AnyNode;
awaitReady: () => Effect.Effect<Handle, ReadyTimedOut | ChildExited | HandleSpent | UnaddressedNode | NodeUnreachable | ProtocolUnanswered | ServiceNotReady | ServiceNotServed | PlatformError | ConfigError>;
handoff: () => Effect.Effect<void, HandleNotReady | AssumeTokenMismatch | AssumeTokenReused | AssumeNotReady | NodeUnreachable | UnaddressedNode | HandleSpent | PlatformError>;
}
handle.Handle.awaitReady: () => Effect.Effect<Handle, ReadyTimedOut | ChildExited | HandleSpent | UnaddressedNode | NodeUnreachable | ProtocolUnanswered | ServiceNotReady | ServiceNotServed | PlatformError | ConfigError>Wait until Ready (allReady or ready.services) is proven cross-process.
awaitReady()),
import EffectEffect.const flatMap: {
<A, B, E1, R1>(
f: (a: A) => Effect<B, E1, R1>
): <E, R>(
self: Effect<A, E, R>
) => Effect<B, E1 | E, R1 | R>
<A, E, R, B, E1, R1>(
self: Effect<A, E, R>,
f: (a: A) => Effect<B, E1, R1>
): Effect<B, E | E1, R | R1>
}
Chains effects to produce new Effect instances, useful for combining
operations that depend on previous results.
When to use
Use when you need to chain multiple effects, ensuring that each
step produces a new Effect while flattening any nested effects that may
occur.
Details
flatMap lets you sequence effects so that the result of one effect can be
used in the next step. It is similar to flatMap used with arrays but works
specifically with Effect instances, allowing you to avoid deeply nested
effect structures.
Since effects are immutable, flatMap always returns a new effect instead of
changing the original one.
Example (Choosing flatMap syntax variants)
import { Effect, pipe } from "effect"
const myEffect = Effect.succeed(1)
const transformation = (n: number) => Effect.succeed(n + 1)
const flatMappedWithPipe = pipe(myEffect, Effect.flatMap(transformation))
const flatMappedWithDataFirst = Effect.flatMap(myEffect, transformation)
const flatMappedWithMethod = myEffect.pipe(Effect.flatMap(transformation))
Example (Sequencing dependent effects)
import { Data, Effect, pipe } from "effect"
class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, DiscountRateError> =>
discountRate === 0
? Effect.fail(new DiscountRateError())
: Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Chaining the fetch and discount application using `flatMap`
const finalAmount = pipe(
fetchTransactionAmount,
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(finalAmount).then(console.log)
// Output: 95
flatMap((handle: Handle(parameter) handle: {
token: Redacted.Redacted<string>;
node: AnyNode;
awaitReady: () => Effect.Effect<Handle, ReadyTimedOut | ChildExited | HandleSpent | UnaddressedNode | NodeUnreachable | ProtocolUnanswered | ServiceNotReady | ServiceNotServed | PlatformError | ConfigError>;
handoff: () => Effect.Effect<void, HandleNotReady | AssumeTokenMismatch | AssumeTokenReused | AssumeNotReady | NodeUnreachable | UnaddressedNode | HandleSpent | PlatformError>;
}
handle) => handle: Handle(parameter) handle: {
token: Redacted.Redacted<string>;
node: AnyNode;
awaitReady: () => Effect.Effect<Handle, ReadyTimedOut | ChildExited | HandleSpent | UnaddressedNode | NodeUnreachable | ProtocolUnanswered | ServiceNotReady | ServiceNotServed | PlatformError | ConfigError>;
handoff: () => Effect.Effect<void, HandleNotReady | AssumeTokenMismatch | AssumeTokenReused | AssumeNotReady | NodeUnreachable | UnaddressedNode | HandleSpent | PlatformError>;
}
handle.Handle.handoff: () => Effect.Effect<void, HandleNotReady | AssumeTokenMismatch | AssumeTokenReused | AssumeNotReady | NodeUnreachable | UnaddressedNode | HandleSpent | PlatformError>Call
Node.assume
, then unref the child so the launcher scope may close.
handoff()),
),
{ concurrency?: Concurrency | undefinedconcurrency: 1 },
);
yield* import EffectEffect.const logInfo: (
...message: ReadonlyArray<any>
) => Effect<void>
Logs one or more messages at the INFO level.
Example (Logging information)
import { Effect } from "effect"
const program = Effect.gen(function*() {
yield* Effect.logInfo("Application starting up")
yield* Effect.logInfo("Config loaded:", "production", "Port:", 3000)
// Useful for general information
const version = "1.2.3"
yield* Effect.logInfo("Application version:", version)
})
Effect.runPromise(program)
// Output:
// timestamp=2023-... level=INFO message="Application starting up"
// timestamp=2023-... level=INFO message="Config loaded: production Port: 3000"
// timestamp=2023-... level=INFO message="Application version: 1.2.3"
logInfo("Launcher.up complete");
}),
);