The Scope module provides resource management for Effect workflows. A Scope tracks finalizers and ensures they run when the scope closes.
Types
Scope
Represents a scope that manages finalizers and can fork child scopes.
strategy
ExecutionStrategy.ExecutionStrategy
The execution strategy for running finalizers
CloseableScope
interface CloseableScope extends Scope
A scope that can be explicitly closed with a specified exit value.
Scope.Finalizer
type Finalizer = (exit: Exit.Exit<unknown, unknown>) => Effect.Effect<void>
A finalizer function that receives an Exit value and returns an effect.
exit
Exit.Exit<unknown, unknown>
required
The exit value indicating how the scope closed
An effect performing cleanup
Tag
Scope
A tag representing the current Scope in the environment.
const Scope: Context.Tag<Scope, Scope>
return
Context.Tag<Scope, Scope>
The scope service tag
import { Effect, Scope } from "effect"
const program = Effect.gen(function* () {
const scope = yield* Scope
// Use the scope
})
Constructors
make
Creates a new closeable scope.
const make: (
executionStrategy?: ExecutionStrategy.ExecutionStrategy
) => Effect.Effect<CloseableScope>
executionStrategy
ExecutionStrategy.ExecutionStrategy
How finalizers should execute (defaults to sequential)
return
Effect.Effect<CloseableScope>
An effect that creates a new scope
import { Scope, Effect } from "effect"
const program = Effect.gen(function* () {
const scope = yield* Scope.make()
// Add finalizers to scope
// ...
// Close when done
yield* Scope.close(scope, Exit.succeed(void 0))
})
Operations
addFinalizer
Adds a finalizer to a scope. The finalizer runs when the scope closes.
const addFinalizer: (
self: Scope,
finalizer: Effect.Effect<unknown>
) => Effect.Effect<void>
finalizer
Effect.Effect<unknown>
required
The cleanup effect to run on close
An effect that registers the finalizer
import { Scope, Effect, Console } from "effect"
const program = Effect.gen(function* () {
const scope = yield* Scope
yield* Scope.addFinalizer(
scope,
Console.log("Cleaning up!")
)
// When scope closes, "Cleaning up!" will be logged
})
Use addFinalizer when the finalizer doesn’t need to know the exit value. For exit-aware finalizers, use addFinalizerExit.
addFinalizerExit
Adds a finalizer that receives the exit value when the scope closes.
const addFinalizerExit: (
self: Scope,
finalizer: Scope.Finalizer
) => Effect.Effect<void>
A function receiving the exit value
An effect that registers the finalizer
import { Scope, Effect, Exit, Console } from "effect"
const program = Effect.gen(function* () {
const scope = yield* Scope
yield* Scope.addFinalizerExit(
scope,
(exit) => Exit.match(exit, {
onSuccess: () => Console.log("Success cleanup"),
onFailure: (cause) => Console.log("Failure cleanup")
})
)
})
close
Closes a scope with the specified exit value, running all finalizers.
const close: (
self: CloseableScope,
exit: Exit.Exit<unknown, unknown>
) => Effect.Effect<void>
exit
Exit.Exit<unknown, unknown>
required
The exit value to pass to finalizers
An effect that closes the scope
import { Scope, Effect, Exit } from "effect"
const program = Effect.gen(function* () {
const scope = yield* Scope.make()
// Add finalizers...
// Close the scope
yield* Scope.close(scope, Exit.succeed(void 0))
})
fork
Forks a new child scope with the specified execution strategy.
const fork: (
self: Scope,
strategy: ExecutionStrategy.ExecutionStrategy
) => Effect.Effect<CloseableScope>
strategy
ExecutionStrategy.ExecutionStrategy
required
How to execute the child scope’s finalizers
return
Effect.Effect<CloseableScope>
An effect producing a child scope
The child scope will automatically close when the parent scope closes.
extend
Extends the scope of an effect without closing when it completes.
const extend: {
(scope: Scope): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Scope>>
<A, E, R>(effect: Effect.Effect<A, E, R>, scope: Scope): Effect.Effect<A, E, Exclude<R, Scope>>
}
effect
Effect.Effect<A, E, R>
required
An effect that requires a Scope
return
Effect.Effect<A, E, Exclude<R, Scope>>
The effect with scope provided
import { Scope, Effect } from "effect"
const acquireResource = Effect.gen(function* () {
const scope = yield* Scope
yield* Scope.addFinalizer(scope, Effect.log("Resource released"))
return "resource"
})
const program = Effect.gen(function* () {
const outerScope = yield* Scope.make()
// Extend the resource into the outer scope
const resource = yield* Scope.extend(outerScope)(acquireResource)
// Resource won't be released until outerScope closes
yield* Effect.log(`Using ${resource}`)
})
use
Provides a closeable scope to an effect and closes it when the effect completes.
const use: {
(scope: CloseableScope): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Scope>>
<A, E, R>(effect: Effect.Effect<A, E, R>, scope: CloseableScope): Effect.Effect<A, E, Exclude<R, Scope>>
}
effect
Effect.Effect<A, E, R>
required
An effect that requires a Scope
The scope to provide and close
return
Effect.Effect<A, E, Exclude<R, Scope>>
The effect with scope managed automatically
import { Scope, Effect } from "effect"
const acquireResource = Effect.gen(function* () {
const scope = yield* Scope
yield* Scope.addFinalizer(scope, Effect.log("Cleanup"))
return "resource"
})
const program = Effect.gen(function* () {
const scope = yield* Scope.make()
// Scope will close when acquireResource completes
const resource = yield* Scope.use(scope)(acquireResource)
return resource
})
Common Patterns
Resource Management
Scopes are commonly used with Effect.acquireRelease for safe resource management:
import { Effect, Scope } from "effect"
const resource = Effect.acquireRelease(
Effect.sync(() => {
console.log("Acquiring resource")
return { id: 1 }
}),
(res) => Effect.sync(() => {
console.log(`Releasing resource ${res.id}`)
})
)
const program = Effect.scoped(
Effect.gen(function* () {
const res = yield* resource
yield* Effect.log(`Using resource ${res.id}`)
})
)
// Acquiring resource
// Using resource 1
// Releasing resource 1
Manual Scope Control
For more control over scope lifetime:
import { Effect, Scope, Exit } from "effect"
const program = Effect.gen(function* () {
const scope = yield* Scope.make()
yield* Scope.addFinalizer(
scope,
Effect.log("First finalizer")
)
yield* Scope.addFinalizer(
scope,
Effect.log("Second finalizer")
)
// Do work...
// Manually close when ready
yield* Scope.close(scope, Exit.succeed(void 0))
})
// Output:
// Second finalizer
// First finalizer
Finalizers run in reverse order of registration (LIFO - Last In, First Out).