Skip to content

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.

34 exports Added in v2.0.0 Source

Constructors

empty

Added in v2.0.0 Source

Returns an empty Context.

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

Added in v2.0.0 Source

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)

make

Added in v2.0.0 Source

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 })

Reference

Added in v3.11.0 Source

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 2048

Example

(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 -1

Tag

Added in v2.0.0 Source

Signature

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

Added in v2.0.0 Source

Signature

declare const unsafeMake: <Services>(unsafeMap: Map<string, any>) => Context<Services>;

Getters

get

Added in v2.0.0 Source

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 })

getOption

Added in v2.0.0 Source

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())

getOrElse

Added in v3.7.0 Source

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

isContext

Added in v2.0.0 Source

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

Added in v3.11.0 Source

Checks if the provided argument is a Reference.

Signature

declare const isReference: (u: unknown) => u is Reference<any, any>;

isTag

Added in v2.0.0 Source

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

Context interface

Added in v2.0.0 Source

Signature

interface Context<in Services> extends Equal, Pipeable, Inspectable {
  readonly [TypeId]: {
    readonly _Services: Contravariant<Services>;
  };
  readonly unsafeMap: Map<string, any>;
}

ReadonlyTag interface

Added in v3.5.9 Source

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;
}

Reference interface

Added in v3.11.0 Source

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

Added in v3.11.0 Source

Signature

interface ReferenceClass<Self, Id extends string, Type> extends Reference<Self, Type> {
  constructor(_: never);
  readonly key: Id;
}

Tag interface

Added in v3.5.9 Source

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;
}

TagClass interface

Added in v2.0.0 Source

Signature

interface TagClass<Self, Id extends string, Type> extends Tag<Self, Type> {
  constructor(_: never);
  readonly key: Id;
}

TagClassShape interface

Added in v2.0.0 Source

Signature

interface TagClassShape<Id, Shape> {
  readonly [TagTypeId]: typeof TagTypeId;
  Id: Id;
  readonly Type: Shape;
}

TagUnify interface

Added in v2.0.0 Source

Signature

interface TagUnify<
  A extends {
    [typeSymbol]?: any;
  },
> {
  Tag?: () => Extract<A[typeof typeSymbol], Tag<any, any>>;
}

TagUnifyIgnore interface

Added in v2.0.0 Source

Signature

interface TagUnifyIgnore {
  Effect?: true;
  Either?: true;
  Option?: true;
}

ValidTagsById type

Added in v2.0.0 Source

Signature

type ValidTagsById<R> = R extends infer S ? Tag<S, any> : never;

Other

add

Added in v2.0.0 Source

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 })

merge

Added in v2.0.0 Source

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 })

mergeAll

Added in v3.12.0 Source

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" })

omit

Added in v2.0.0 Source

Signature

declare const omit: <Tags extends ReadonlyArray<Tag<any, any>>>(
  ...tags: Tags
) => <Services>(
  self: Context<Services>,
) => Context<Exclude<Services, Tag.Identifier<Tags[number]>>>;

pick

Added in v2.0.0 Source

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())

Tag

Added in v2.0.0 Source

Symbol

Signature

declare const ReferenceTypeId: unique symbol;

ReferenceTypeId type

Added in v3.11.0 Source

Signature

type ReferenceTypeId = typeof ReferenceTypeId;

TagTypeId

Added in v2.0.0 Source

Signature

declare const TagTypeId: unique symbol;

TagTypeId type

Added in v2.0.0 Source

Signature

type TagTypeId = typeof TagTypeId;

TypeId type

Added in v2.0.0 Source

Signature

type TypeId = typeof TypeId;

Unsafe

unsafeGet

Added in v2.0.0 Source

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))