Skip to content

Effect

A module’s body and an operation’s handler are Effects. A plugin can read the capability manager as an Effect service, contribute layers of its own, and name the services an operation needs.

Inside a module, the capability manager is Capability.Service. Capability.get, getAll and waitFor read it for you; yield the service itself when you need the manager.

import * as
import Effect
Effect
from 'effect/Effect';
import * as
import Capability
Capability
from '@dxos/app-framework/Capability';
import * as
import AppCapabilities
AppCapabilities
from '@dxos/app-toolkit/AppCapabilities';
// Inside a module, the capability manager is the `Capability.Service` in
// context; `Capability.get` and friends read it.
export const
const CountSchemasModule: (props: void) => Effect.Effect<never[], never, Capability.Service | Scope>
CountSchemasModule
=
import Capability
Capability
.
const makeModule: <void, never[], never, Capability.Service>(fn: (props: void) => Effect.Effect<never[], never, Capability.Service | Scope>) => (props: void) => Effect.Effect<never[], never, Capability.Service | Scope>

Helper to define a capability module with explicit typing. Wraps the default export function to provide better type inference and make the pattern explicit.

This helper provides explicit typing for the module activation function, making it clear that the function should:

  • Access declared requires via yield* (or the ambient Capability.Service/Plugin.Service)
  • Return an array of typed

Contribution

s (see

contribute

/

contributeAll

)

@example

// Module without options - single capability
export default Capability.makeModule(
Effect.fnUntraced(function* () {
const client = yield* ClientCapabilities.Client;
return Capability.contribute(Capabilities.SettingsStore, store);
})
);
// Module with multiple capabilities
export default Capability.makeModule(
Effect.fnUntraced(function* () {
return [
Capability.contribute(Capabilities.SettingsStore, store),
Capability.contribute(Capabilities.Translations, translations),
];
})
);
// Module with required options (context accessed via layer)
export default Capability.makeModule(
Effect.fnUntraced(function* (props: { observability: boolean }) {
const invoker = yield* Capabilities.OperationInvoker;
return Capability.contribute(Capabilities.OperationHandler, ...);
})
);
// Module with scoped resources (closed automatically on deactivation)
export default Capability.makeModule(
Effect.fnUntraced(function* () {
const scope = yield* Scope.Scope;
yield* Scope.addFinalizer(scope, Effect.sync(() => cleanup()));
return Capability.contribute(Capabilities.MyCapability, implementation);
})
);

makeModule
(() =>
import Effect
Effect
.
const gen: <Effect.Effect<CapabilityManager, never, Capability.Service>, never[]>(f: () => Generator<Effect.Effect<CapabilityManager, never, Capability.Service>, never[], never>) => Effect.Effect<never[], never, Capability.Service> (+1 overload)

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}`
})
await Effect.runPromise(program) // => "Final amount to charge: 96"

@stability ― stable

@category ― constructors

@since ― 2.0.0

gen
(function* () {
const
const manager: CapabilityManager
manager
= yield*
import Capability
Capability
.
class Service

Effect Context.Tag for accessing CapabilityManager via the Effect layer system. This allows capability modules to access the capability manager without having it passed as an argument.

Service
;
const
const schemas: AnyEntity[]
schemas
=
const manager: CapabilityManager
manager
.
CapabilityManager.getAll<AppCapabilities.Schema>(interfaceDef: Capability.InterfaceDef<AppCapabilities.Schema>): AppCapabilities.Schema[]

Get capabilities from the capability manager.

@returns ― An array of capabilities.

getAll
(
import AppCapabilities
AppCapabilities
.
const Schema: Capability.MultiTag<AppCapabilities.Schema, "org.dxos.app-framework.capability.schema">

@category ― Capability

Schema
).
Array<Schema>.flat<AppCapabilities.Schema[], 1>(this: AppCapabilities.Schema[], depth?: 1 | undefined): AnyEntity[]

Returns a new array with all sub-array elements concatenated into it recursively up to the specified depth.

@param ― depth The maximum recursion depth

flat
();
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@see ― source

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@since ― v0.1.100

log
(
const schemas: AnyEntity[]
schemas
.
Array<AnyEntity>.length: number

Gets or sets the length of the array. This is a number one higher than the highest index in the array.

length
);
return [];
}),
);

A service an operation uses comes from a layer. Contribute a LayerSpec to Capabilities.LayerSpec: it says what the layer provides, what it requires and how widely one instance is shared. With affinity: 'application' the app builds the layer once and every operation shares it.

import * as
import Context
Context
from 'effect/Context';
import * as
import Effect
Effect
from 'effect/Effect';
import * as
import Layer
Layer
from 'effect/Layer';
import * as
import Capabilities
Capabilities
from '@dxos/app-framework/Capabilities';
import * as
import Capability
Capability
from '@dxos/app-framework/Capability';
import * as
import LayerSpec
LayerSpec
from '@dxos/compute/LayerSpec';
export class
class LinkChecker
LinkChecker
extends
import Context
Context
.
const Service: <LinkChecker, {
readonly check: (url: string) => Effect.Effect<boolean>;
}>() => <Identifier, E, R, Args>(id: Identifier, options?: {
readonly make?: ((...args: Args) => Effect.Effect<{
readonly check: (url: string) => Effect.Effect<boolean>;
}, E, R>) | Effect.Effect<{
readonly check: (url: string) => Effect.Effect<boolean>;
}, E, R> | undefined;
} | undefined) => Context.ServiceClass<LinkChecker, Identifier, {
readonly check: (url: string) => Effect.Effect<boolean>;
}> & ([...] extends [...] ? unknown : {
...;
}) (+2 overloads)

Creates a Context service key.

When to use

Use when you need to define a context service key for a dependency that must be provided by the surrounding context.

Details

Call Context.Service("Key") for a function-style key, or use the two-stage form Context.Service<Self, Shape>()("Key") for class-style service declarations. The returned key can be yielded as an Effect and passed to Context.make, Context.add, and the Context getter functions.

Gotchas

The string key is the runtime identity of the service. Reusing the same key string for unrelated services makes them occupy the same slot in a Context.

Example (Creating service keys)

import { Context } from "effect"
// Create a simple service
const Database = Context.Service<{
query: (sql: string) => string
}>("Database")
// Create a service class
class Config extends Context.Service<Config, {
port: number
}>()("Config") {}
// Use the services to create contexts
const db = Context.make(Database, {
query: (sql) => `Result: ${sql}`
})
const config = Context.make(Config, { port: 8080 })
Context.get(db, Database).query("SELECT 1") // => "Result: SELECT 1"
Context.get(config, Config).port // => 8080

@see ― Reference for service keys with default values

@stability ― stable

@category ― services

@since ― 4.0.0

Service
<
class LinkChecker
LinkChecker
,
{ readonly
check: (url: string) => Effect.Effect<boolean>
check
: (
url: string
url
: string) =>
import Effect
Effect
.
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.

@stability ― stable

@category ― models

@since ― 2.0.0

Effect
<boolean> }
>()('com.example.plugin.stash.LinkChecker') {}
const
const LinkCheckerLive: Layer.Layer<LinkChecker, never, never>
LinkCheckerLive
=
import Layer
Layer
.
const succeed: <LinkChecker, {
readonly check: (url: string) => Effect.Effect<boolean>;
}>(service: Context.Key<LinkChecker, {
readonly check: (url: string) => Effect.Effect<boolean>;
}>, resource: {
readonly check: (url: string) => Effect.Effect<boolean>;
}) => Layer.Layer<LinkChecker, never, never> (+1 overload)

Constructs a layer that provides a single service from an already available value.

When to use

Use when you need a Layer that provides a service from an already constructed implementation without effectful acquisition.

Example (Creating a layer from a service implementation)

import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const DatabaseLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Query result: ${sql}`))
})
const program = Database.use((database) => database.query("SELECT 1"))
Effect.runSync(Effect.provide(program, DatabaseLayer)) // => "Query result: SELECT 1"

@see ― sync for constructing layers from lazy values

@stability ― stable

@category ― constructors

@since ― 2.0.0

succeed
(
class LinkChecker
LinkChecker
, {
check: (url: string) => Effect.Effect<boolean>
check
: (
url: string
url
) =>
import Effect
Effect
.
const succeed: <boolean>(value: boolean) => Effect.Effect<boolean, never, never>

Creates an Effect that always succeeds with a given value.

When to use

Use when an effect should complete successfully with a specific value without any errors or external dependencies.

Example (Creating a successful effect)

import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)
Effect.runSync(success) // => 42

@see ― fail to create an effect that represents a failure.

@stability ― stable

@category ― constructors

@since ― 2.0.0

succeed
(
url: string
url
.
String.startsWith(searchString: string, position?: number): boolean

Returns true if the sequence of elements of searchString converted to a String is the same as the corresponding elements of this object (converted to a String) starting at position. Otherwise returns false.

startsWith
('https://')),
});
// A layer spec says what the layer provides and how widely it is shared; the
// runtime builds it for any operation that declares the service.
export const
const LinkCheckerModule: (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.layerSpec", "multi">>, never, Scope>
LinkCheckerModule
=
import Capability
Capability
.
const makeModule: <void, Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.layerSpec", "multi">>, never, never>(fn: (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.layerSpec", "multi">>, never, Scope>) => (props: void) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.layerSpec", "multi">>, never, Scope>

Helper to define a capability module with explicit typing. Wraps the default export function to provide better type inference and make the pattern explicit.

This helper provides explicit typing for the module activation function, making it clear that the function should:

  • Access declared requires via yield* (or the ambient Capability.Service/Plugin.Service)
  • Return an array of typed

Contribution

s (see

contribute

/

contributeAll

)

@example

// Module without options - single capability
export default Capability.makeModule(
Effect.fnUntraced(function* () {
const client = yield* ClientCapabilities.Client;
return Capability.contribute(Capabilities.SettingsStore, store);
})
);
// Module with multiple capabilities
export default Capability.makeModule(
Effect.fnUntraced(function* () {
return [
Capability.contribute(Capabilities.SettingsStore, store),
Capability.contribute(Capabilities.Translations, translations),
];
})
);
// Module with required options (context accessed via layer)
export default Capability.makeModule(
Effect.fnUntraced(function* (props: { observability: boolean }) {
const invoker = yield* Capabilities.OperationInvoker;
return Capability.contribute(Capabilities.OperationHandler, ...);
})
);
// Module with scoped resources (closed automatically on deactivation)
export default Capability.makeModule(
Effect.fnUntraced(function* () {
const scope = yield* Scope.Scope;
yield* Scope.addFinalizer(scope, Effect.sync(() => cleanup()));
return Capability.contribute(Capabilities.MyCapability, implementation);
})
);

makeModule
(() =>
import Effect
Effect
.
const succeed: <Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.layerSpec", "multi">>>(value: Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.layerSpec", "multi">>) => Effect.Effect<Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.layerSpec", "multi">>, never, never>

Creates an Effect that always succeeds with a given value.

When to use

Use when an effect should complete successfully with a specific value without any errors or external dependencies.

Example (Creating a successful effect)

import { Effect } from "effect"
// Creating an effect that represents a successful scenario
//
// ┌─── Effect<number, never, never>
// ▼
const success = Effect.succeed(42)
Effect.runSync(success) // => 42

@see ― fail to create an effect that represents a failure.

@stability ― stable

@category ― constructors

@since ― 2.0.0

succeed
(
import Capability
Capability
.
const contribute: <Capability.MultiTag<LayerSpec.LayerSpec, "org.dxos.app-framework.capability.layerSpec">>(capability: Capability.MultiTag<LayerSpec.LayerSpec, "org.dxos.app-framework.capability.layerSpec">, implementation: LayerSpec.LayerSpec) => Capability.Contribution<Capability.CapabilityIdentifier<"org.dxos.app-framework.capability.layerSpec", "multi">> (+1 overload)
contribute
(
import Capabilities
Capabilities
.
const LayerSpec: Capability.MultiTag<LayerSpec.LayerSpec, "org.dxos.app-framework.capability.layerSpec">

Layer specification contributed by plugins.

Plugins contribute

LayerSpec.LayerSpec

entries that are collected by the process-manager module and composed into a

LayerStack

which backs the

ProcessManagerRuntime

's service resolver.

@category ― Capability

LayerSpec
,
import LayerSpec
LayerSpec
.
const make: <{
readonly affinity: "application";
readonly requires: readonly [];
readonly provides: readonly [typeof LinkChecker];
}>(opts: {
readonly affinity: "application";
readonly requires: readonly [];
readonly provides: readonly [typeof LinkChecker];
}, make: (context: LayerSpec.LayerContext) => Layer.Layer<LinkChecker, never, never>) => LayerSpec.LayerSpec

Make a service.

make
(
{
affinity: "application"
affinity
: 'application',
requires: readonly []
requires
: [],
provides: readonly [typeof LinkChecker]
provides
: [
class LinkChecker
LinkChecker
] },
() =>
const LinkCheckerLive: Layer.Layer<LinkChecker, never, never>
LinkCheckerLive
,
),
),
),
);

An operation lists the services it needs in services. When it is invoked, the runtime builds them from the layer specs plugins contributed, and the handler yields them like any Effect service.

import * as
import Effect
Effect
from 'effect/Effect';
import * as
import Schema
Schema
from 'effect/Schema';
import * as
import Operation
Operation
from '@dxos/compute/Operation';
import {
import DXN
DXN
} from '@dxos/echo';
export const
const CheckLink: Operation.Definition<{
readonly url: string;
}, {
readonly ok: boolean;
}, LinkChecker>
CheckLink
=
import Operation
Operation
.
const make: <{
readonly meta: {
readonly key: DXN.DXN;
readonly name: "Check link";
};
readonly input: Schema.Struct<{
readonly url: Schema.String;
}>;
readonly output: Schema.Struct<{
readonly ok: Schema.Boolean;
}>;
readonly services: readonly [typeof LinkChecker];
}>(props: {
readonly meta: {
readonly key: DXN.DXN;
readonly name: "Check link";
};
readonly input: Schema.Struct<{
readonly url: Schema.String;
}>;
readonly output: Schema.Struct<{
readonly ok: Schema.Boolean;
}>;
readonly services: readonly [typeof LinkChecker];
}) => Operation.Definition<...>

Creates a new Operation definition. Applies default executionMode of 'async' if not specified. The returned type preserves the literal types of props (including services).

make
({
meta: {
readonly key: DXN.DXN;
readonly name: "Check link";
}
meta
: {
key: DXN.DXN
key
:
import DXN
DXN
.
const make: <"com.example.operation.checkLink">(nsid: "com.example.operation.checkLink", version?: string) => DXN.DXN

Constructs a DXN from an NSID (and optional version). Throws if the result is not a valid DXN. Use tryMake for non-throwing string parsing.

Static NSID strings are validated at compile time via

Name

: the final segment must be camelCase (no hyphens). Template-literal strings with a runtime prefix are accepted here but still validated at runtime.

@example make('org.dxos.type.calendar') → 'dxn:org.dxos.type.calendar'

@example make('org.dxos.type.calendar', '1.0.0') → 'dxn:org.dxos.type.calendar:1.0.0'

make
('com.example.operation.checkLink'),
name: "Check link"
name
: 'Check link',
},
input: Schema.Struct<{
readonly url: Schema.String;
}>
input
:
import Schema
Schema
.
function Struct<{
readonly url: Schema.String;
}>(fields: {
readonly url: Schema.String;
}): Schema.Struct<{
readonly url: Schema.String;
}>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types. Declared fields may be inherited and are copied to own properties in the output. The __proto__ field is accepted only when it is an own property. Parsing does not guarantee that output keys retain their input order.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 }) // => { name: "Alice", age: 30 }

@stability ― stable

@category ― constructors

@since ― 3.10.0

Struct
({
url: Schema.String
url
:
import Schema
Schema
.
const String: Schema.String

Type-level representation of

String

.

Schema for string values. Validates that the input is typeof "string".

@stability ― stable

@category ― models

@since ― 4.0.0

@stability ― stable

@category ― schemas

@since ― 4.0.0

String
}),
output: Schema.Struct<{
readonly ok: Schema.Boolean;
}>
output
:
import Schema
Schema
.
function Struct<{
readonly ok: Schema.Boolean;
}>(fields: {
readonly ok: Schema.Boolean;
}): Schema.Struct<{
readonly ok: Schema.Boolean;
}>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types. Declared fields may be inherited and are copied to own properties in the output. The __proto__ field is accepted only when it is an own property. Parsing does not guarantee that output keys retain their input order.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 }) // => { name: "Alice", age: 30 }

@stability ― stable

@category ― constructors

@since ― 3.10.0

Struct
({
ok: Schema.Boolean
ok
:
import Schema
Schema
.
const Boolean: Schema.Boolean

Type-level representation of

Boolean

.

Schema for boolean values. Validates that the input is typeof "boolean".

When to use

Use to validate values that are already JavaScript booleans.

@stability ― stable

@category ― models

@since ― 4.0.0

@see ― BooleanFromBit for a schema that decodes bit literals 0 or 1 into a boolean

@stability ― stable

@category ― schemas

@since ― 4.0.0

Boolean
}),
services: readonly [typeof LinkChecker]
services
: [
class LinkChecker
LinkChecker
],
});
export const
const checkLink: Operation.WithHandler<Operation.Definition<{
readonly url: string;
}, {
readonly ok: boolean;
}, LinkChecker>>
checkLink
=
const CheckLink: Operation.Definition<{
readonly url: string;
}, {
readonly ok: boolean;
}, LinkChecker>
CheckLink
.
Pipeable.pipe<Operation.Definition<{
readonly url: string;
}, {
readonly ok: boolean;
}, LinkChecker>, Operation.WithHandler<Operation.Definition<{
readonly url: string;
}, {
readonly ok: boolean;
}, LinkChecker>>>(this: Operation.Definition<{
readonly url: string;
}, {
readonly ok: boolean;
}, LinkChecker>, ab: (_: Operation.Definition<{
readonly url: string;
}, {
readonly ok: boolean;
}, LinkChecker>) => Operation.WithHandler<Operation.Definition<{
readonly url: string;
}, {
readonly ok: boolean;
}, LinkChecker>>): Operation.WithHandler<...> (+21 overloads)
pipe
(
import Operation
Operation
.
const withHandler: <Operation.Definition<{
readonly url: string;
}, {
readonly ok: boolean;
}, LinkChecker>, never>(handler: Operation.Handler<{
readonly url: string;
}, {
readonly ok: boolean;
}, never, LinkChecker | Operation.Service>) => (op: Operation.Definition<{
readonly url: string;
}, {
readonly ok: boolean;
}, LinkChecker>) => Operation.WithHandler<Operation.Definition<{
readonly url: string;
}, {
readonly ok: boolean;
}, LinkChecker>> (+1 overload)
withHandler
(({
url: string
url
}) =>
import Effect
Effect
.
const gen: <Effect.Effect<boolean, never, never> | Effect.Effect<{
readonly check: (url: string) => Effect.Effect<boolean>;
}, never, LinkChecker>, {
ok: boolean;
}>(f: () => Generator<Effect.Effect<boolean, never, never> | Effect.Effect<{
readonly check: (url: string) => Effect.Effect<boolean>;
}, never, LinkChecker>, {
ok: boolean;
}, never>) => Effect.Effect<{
ok: boolean;
}, never, LinkChecker> (+1 overload)

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}`
})
await Effect.runPromise(program) // => "Final amount to charge: 96"

@stability ― stable

@category ― constructors

@since ― 2.0.0

gen
(function* () {
const
const checker: {
readonly check: (url: string) => Effect.Effect<boolean>;
}
checker
= yield*
class LinkChecker
LinkChecker
;
return {
ok: boolean
ok
: yield*
const checker: {
readonly check: (url: string) => Effect.Effect<boolean>;
}
checker
.
check: (url: string) => Effect.Effect<boolean>
check
(
url: string
url
) };
}),
),
);

An operation whose services have no layer fails when invoked, not when the plugin loads. Testing a plugin catches it.

  • Never run an Effect with Effect.runPromise inside a module. Return it, so the plugin manager can interrupt it when the module deactivates.
  • A layer spec’s service is in-process. A service that must run on EDGE needs a layer there too.