Context
This module provides a data structure called Context that can be used for dependency injection in effectful programs. It is essentially a table mapping Tags to their implementations (called Services), and can be used to manage dependencies in a type-safe way. The Context data structure is essentially a way of providing access to a set of related services that can be passed around as a single unit. This module provides functions to create, modify, and query the contents of a Context, as well as a number of utility types for working with tags and services.
Constructors
Signature
declare const empty: () => Context<never>;Example
import * as assert from "node:assert"
import { Context } from "effect"
assert.strictEqual(Context.isContext(Context.empty()), true)GenericTag
Creates a new Tag instance with the specified key.
Signature
declare const GenericTag: <Identifier, Service = Identifier>(
key: string,
) => Tag<Identifier, Service>;Example
import * as assert from "node:assert"
import { Context } from "effect"
assert.strictEqual(Context.GenericTag("PORT").key === Context.GenericTag("PORT").key, true)Creates a new Context with a single service associated to the tag.
Signature
declare const make: <I, S>(tag: Tag<I, S>, service: Types.NoInfer<S>) => Context<I>;Example
import * as assert from "node:assert"
import { Context } from "effect"
const Port = Context.GenericTag<{ PORT: number }>("Port")
const Services = Context.make(Port, { PORT: 8080 })
assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 })Creates a context tag with a default value.
Details
Context.Reference allows you to create a tag that can hold a value. You can provide a default value for the service, which will automatically be used when the context is accessed, or override it with a custom implementation when needed.
Signature
declare const Reference: <Self>() => <Id extends string, Service>(
id: Id,
options: {
readonly defaultValue: () => Service;
},
) => ReferenceClass<Self, Id, Service>;Example
(Declaring a Tag with a default value)
import * as assert from "node:assert"
import { Context, Effect } from "effect"
class SpecialNumber extends Context.Reference<SpecialNumber>()("SpecialNumber", {
defaultValue: () => 2048,
}) {}
// โโโโ Effect<void, never, never>
// โผ
const program = Effect.gen(function* () {
const specialNumber = yield* SpecialNumber
console.log(`The special number is ${specialNumber}`)
})
// No need to provide the SpecialNumber implementation
Effect.runPromise(program)
// Output: The special number is 2048Example
(Overriding the default value)
import { Context, Effect } from "effect"
class SpecialNumber extends Context.Reference<SpecialNumber>()("SpecialNumber", {
defaultValue: () => 2048,
}) {}
const program = Effect.gen(function* () {
const specialNumber = yield* SpecialNumber
console.log(`The special number is ${specialNumber}`)
})
Effect.runPromise(program.pipe(Effect.provideService(SpecialNumber, -1)))
// Output: The special number is -1Signature
declare const Tag: <Id extends string>(id: Id) => <Self, Shape>() => TagClass<Self, Id, Shape>;Example
import * as assert from "node:assert"
import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<MyTag, { readonly myNum: number }>() {
static Live = Layer.succeed(this, { myNum: 108 })
}unsafeMake
Signature
declare const unsafeMake: <Services>(unsafeMap: Map<string, any>) => Context<Services>;Getters
Get a service from the context that corresponds to the given tag.
Signature
declare const get: {
<I, S>(tag: Reference<I, S>): <Services>(self: Context<Services>) => S;
<Services, I, S>(tag: Tag<I, S>): (self: Context<Services>) => S;
<Services, I, S>(self: Context<Services>, tag: Reference<I, S>): S;
<Services, I, S>(self: Context<Services>, tag: Tag<I, S>): S;
};Example
import * as assert from "node:assert"
import { pipe, Context } from "effect"
const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")
const Services = pipe(Context.make(Port, { PORT: 8080 }), Context.add(Timeout, { TIMEOUT: 5000 }))
assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 })Get the value associated with the specified tag from the context wrapped in an Option object. If the tag is not found, the Option object will be None.
Signature
declare const getOption: {
<S, I>(tag: Tag<I, S>): <Services>(self: Context<Services>) => Option<S>;
<Services, S, I>(self: Context<Services>, tag: Tag<I, S>): Option<S>;
};Example
import * as assert from "node:assert"
import { Context, Option } from "effect"
const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")
const Services = Context.make(Port, { PORT: 8080 })
assert.deepStrictEqual(Context.getOption(Services, Port), Option.some({ PORT: 8080 }))
assert.deepStrictEqual(Context.getOption(Services, Timeout), Option.none())Get a service from the context that corresponds to the given tag, or use the fallback value.
Signature
declare const getOrElse: {
<S, I, B>(tag: Tag<I, S>, orElse: LazyArg<B>): <Services>(self: Context<Services>) => S | B;
<Services, S, I, B>(self: Context<Services>, tag: Tag<I, S>, orElse: LazyArg<B>): S | B;
};Guards
Checks if the provided argument is a Context.
Signature
declare const isContext: (input: unknown) => input is Context<never>;Example
import * as assert from "node:assert"
import { Context } from "effect"
assert.strictEqual(Context.isContext(Context.empty()), true)isReference
Checks if the provided argument is a Reference.
Signature
declare const isReference: (u: unknown) => u is Reference<any, any>;Checks if the provided argument is a Tag.
Signature
declare const isTag: (input: unknown) => input is Tag<any, any>;Example
import * as assert from "node:assert"
import { Context } from "effect"
assert.strictEqual(Context.isTag(Context.GenericTag("Tag")), true)Models
Signature
interface Context<in Services> extends Equal, Pipeable, Inspectable {
readonly [TypeId]: {
readonly _Services: Contravariant<Services>;
};
readonly unsafeMap: Map<string, any>;
}ReadonlyTag interface
Signature
interface ReadonlyTag<in out Id, out Value>
extends Pipeable, Inspectable, Effect<Value, never, Id> {
readonly _op: "Tag";
readonly [TagTypeId]: {
readonly _Identifier: Invariant<Id>;
readonly _Service: Covariant<Value>;
};
readonly Identifier: Id;
readonly key: string;
readonly Service: Value;
readonly stack?: string;
}Signature
interface Reference<in out Id, in out Value>
extends Pipeable, Inspectable, STM<Value>, Effect<Value> {
readonly _op: "Tag";
readonly [ChannelTypeId]: VarianceStruct<never, unknown, never, unknown, Value, unknown, never>;
readonly [EffectTypeId]: VarianceStruct<Value, never, never>;
[ignoreSymbol]?: TagUnifyIgnore;
readonly [ReferenceTypeId]: typeof ReferenceTypeId;
readonly [SinkTypeId]: VarianceStruct<Value, unknown, never, never, never>;
readonly [STMTypeId]: {
readonly _A: Covariant<Value>;
readonly _E: Covariant<never>;
readonly _R: Covariant<never>;
};
readonly [StreamTypeId]: VarianceStruct<Value, never, never>;
readonly [TagTypeId]: {
readonly _Identifier: Invariant<Id>;
readonly _Service: Invariant<Value>;
};
[typeSymbol]?: unknown;
[unifySymbol]?: TagUnify<Reference<Id, Value>>;
readonly defaultValue: () => Value;
readonly Identifier: Id;
readonly key: string;
readonly Service: Value;
readonly stack?: string;
[iterator](): EffectGenerator<Reference<Id, Value>>;
context(self: Value): Context<Id>;
of(self: Value): Value;
}ReferenceClass interface
Signature
interface ReferenceClass<Self, Id extends string, Type> extends Reference<Self, Type> {
constructor(_: never);
readonly key: Id;
}Signature
interface Tag<in out Id, in out Value>
extends
Pipeable,
Inspectable,
ReadonlyTag<Id, Value>,
STM<Value, never, Id>,
Effect<Value, never, Id> {
readonly _op: "Tag";
[ignoreSymbol]?: TagUnifyIgnore;
readonly [STMTypeId]: {
readonly _A: Covariant<Value>;
readonly _E: Covariant<never>;
readonly _R: Covariant<Id>;
};
readonly [TagTypeId]: {
readonly _Identifier: Invariant<Id>;
readonly _Service: Invariant<Value>;
};
[typeSymbol]?: unknown;
[unifySymbol]?: TagUnify<Tag<Id, Value>>;
readonly Identifier: Id;
readonly key: string;
readonly Service: Value;
readonly stack?: string;
context(self: Value): Context<Id>;
of(self: Value): Value;
}Signature
interface TagClass<Self, Id extends string, Type> extends Tag<Self, Type> {
constructor(_: never);
readonly key: Id;
}TagClassShape interface
Signature
interface TagClassShape<Id, Shape> {
readonly [TagTypeId]: typeof TagTypeId;
Id: Id;
readonly Type: Shape;
}Signature
interface TagUnify<
A extends {
[typeSymbol]?: any;
},
> {
Tag?: () => Extract<A[typeof typeSymbol], Tag<any, any>>;
}TagUnifyIgnore interface
Signature
interface TagUnifyIgnore {
Effect?: true;
Either?: true;
Option?: true;
}ValidTagsById type
Signature
type ValidTagsById<R> = R extends infer S ? Tag<S, any> : never;Other
Adds a service to a given Context.
Signature
declare const add: {
<I, S>(
tag: Tag<I, S>,
service: NoInfer<S>,
): <Services>(self: Context<Services>) => Context<I | Services>;
<Services, I, S>(
self: Context<Services>,
tag: Tag<I, S>,
service: NoInfer<S>,
): Context<Services | I>;
};Example
import * as assert from "node:assert"
import { Context, pipe } from "effect"
const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")
const someContext = Context.make(Port, { PORT: 8080 })
const Services = pipe(someContext, Context.add(Timeout, { TIMEOUT: 5000 }))
assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 })
assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 })Merges two Contexts, returning a new Context containing the services of both.
Signature
declare const merge: {
<R1>(that: Context<R1>): <Services>(self: Context<Services>) => Context<R1 | Services>;
<Services, R1>(self: Context<Services>, that: Context<R1>): Context<Services | R1>;
};Example
import * as assert from "node:assert"
import { Context } from "effect"
const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")
const firstContext = Context.make(Port, { PORT: 8080 })
const secondContext = Context.make(Timeout, { TIMEOUT: 5000 })
const Services = Context.merge(firstContext, secondContext)
assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 })
assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 })Merges any number of Contexts, returning a new Context containing the services of all.
Signature
declare const mergeAll: <T extends Array<unknown>>(
...ctxs: [...{ [K in keyof T]: Context<T[K]> }]
) => Context<T[number]>;Example
import * as assert from "node:assert"
import { Context } from "effect"
const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")
const Host = Context.GenericTag<{ HOST: string }>("Host")
const firstContext = Context.make(Port, { PORT: 8080 })
const secondContext = Context.make(Timeout, { TIMEOUT: 5000 })
const thirdContext = Context.make(Host, { HOST: "localhost" })
const Services = Context.mergeAll(firstContext, secondContext, thirdContext)
assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 })
assert.deepStrictEqual(Context.get(Services, Timeout), { TIMEOUT: 5000 })
assert.deepStrictEqual(Context.get(Services, Host), { HOST: "localhost" })Signature
declare const omit: <Tags extends ReadonlyArray<Tag<any, any>>>(
...tags: Tags
) => <Services>(
self: Context<Services>,
) => Context<Exclude<Services, Tag.Identifier<Tags[number]>>>;Returns a new Context that contains only the specified services.
Signature
declare const pick: <Tags extends ReadonlyArray<Tag<any, any>>>(
...tags: Tags
) => <Services>(self: Context<Services>) => Context<Services & Tag.Identifier<Tags[number]>>;Example
import * as assert from "node:assert"
import { pipe, Context, Option } from "effect"
const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")
const someContext = pipe(
Context.make(Port, { PORT: 8080 }),
Context.add(Timeout, { TIMEOUT: 5000 }),
)
const Services = pipe(someContext, Context.pick(Port))
assert.deepStrictEqual(Context.getOption(Services, Port), Option.some({ PORT: 8080 }))
assert.deepStrictEqual(Context.getOption(Services, Timeout), Option.none())Symbol
ReferenceTypeId
Signature
declare const ReferenceTypeId: unique symbol;ReferenceTypeId type
Signature
type ReferenceTypeId = typeof ReferenceTypeId;Signature
declare const TagTypeId: unique symbol;Signature
type TagTypeId = typeof TagTypeId;Signature
type TypeId = typeof TypeId;Unsafe
Get a service from the context that corresponds to the given tag. This function is unsafe because if the tag is not present in the context, a runtime error will be thrown.
For a safer version see getOption.
Signature
declare const unsafeGet: {
<S, I>(tag: Tag<I, S>): <Services>(self: Context<Services>) => S;
<Services, S, I>(self: Context<Services>, tag: Tag<I, S>): S;
};Example
import * as assert from "node:assert"
import { Context } from "effect"
const Port = Context.GenericTag<{ PORT: number }>("Port")
const Timeout = Context.GenericTag<{ TIMEOUT: number }>("Timeout")
const Services = Context.make(Port, { PORT: 8080 })
assert.deepStrictEqual(Context.unsafeGet(Services, Port), { PORT: 8080 })
assert.throws(() => Context.unsafeGet(Services, Timeout))
Returns an empty
Context.