Skip to main content Link Search Menu Expand Document (external link)

Context overview

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.

Added in v2.0.0


Table of contents


constructors

GenericTag

Creates a new Tag instance with an optional key parameter.

Signature

export declare const GenericTag: <Identifier, Service = Identifier>(key: string) => Tag<Identifier, Service>

Example

import * as Context from "effect/Context"

assert.strictEqual(Context.GenericTag("PORT").key === Context.GenericTag("PORT").key, true)

Added in v2.0.0

Tag

Signature

export declare const Tag: <const Id extends string>(id: Id) => <Self, Shape>() => TagClass<Self, Id, Shape>

Added in v2.0.0

empty

Returns an empty Context.

Signature

export declare const empty: () => Context<never>

Example

import * as Context from "effect/Context"

assert.strictEqual(Context.isContext(Context.empty()), true)

Added in v2.0.0

make

Creates a new Context with a single service associated to the tag.

Signature

export declare const make: <T extends Tag<any, any>>(tag: T, service: Tag.Service<T>) => Context<Tag.Identifier<T>>

Example

import * as Context from "effect/Context"

const Port = Context.GenericTag<{ PORT: number }>("Port")

const Services = Context.make(Port, { PORT: 8080 })

assert.deepStrictEqual(Context.get(Services, Port), { PORT: 8080 })

Added in v2.0.0

unsafeMake

Signature

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

Added in v2.0.0

getters

get

Get a service from the context that corresponds to the given tag.

Signature

export declare const get: {
  <Services, T extends ValidTagsById<Services>>(tag: T): (self: Context<Services>) => Tag.Service<T>
  <Services, T extends ValidTagsById<Services>>(self: Context<Services>, tag: T): Tag.Service<T>
}

Example

import * as Context from "effect/Context"
import { pipe } from "effect/Function"

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

Added in v2.0.0

getOption

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

export 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 Context from "effect/Context"
import * as O from "effect/Option"

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), O.some({ PORT: 8080 }))
assert.deepStrictEqual(Context.getOption(Services, Timeout), O.none())

Added in v2.0.0

guards

isContext

Checks if the provided argument is a Context.

Signature

export declare const isContext: (input: unknown) => input is Context<never>

Example

import * as Context from "effect/Context"

assert.strictEqual(Context.isContext(Context.empty()), true)

Added in v2.0.0

isTag

Checks if the provided argument is a Tag.

Signature

export declare const isTag: (input: unknown) => input is Tag<any, any>

Example

import * as Context from "effect/Context"

assert.strictEqual(Context.isTag(Context.GenericTag("Tag")), true)

Added in v2.0.0

models

Context (interface)

Signature

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

Added in v2.0.0

Tag (interface)

Signature

export interface Tag<in out Id, in out Value> extends Pipeable, Inspectable {
  readonly _tag: "Tag"
  readonly _op: "Tag"
  readonly [TagTypeId]: {
    readonly _Service: Types.Invariant<Value>
    readonly _Identifier: Types.Invariant<Id>
  }
  of(self: Value): Value
  context(self: Value): Context<Id>
  readonly stack?: string | undefined
  readonly key: string
  [Unify.typeSymbol]?: unknown
  [Unify.unifySymbol]?: TagUnify<this>
  [Unify.ignoreSymbol]?: TagUnifyIgnore
}

Added in v2.0.0

TagClass (interface)

Signature

export interface TagClass<Self, Id, Type> extends Tag<Self, Type> {
  new (_: never): TagClassShape<Id, Type>
}

Added in v2.0.0

TagClassShape (interface)

Signature

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

Added in v2.0.0

TagUnify (interface)

Signature

export interface TagUnify<A extends { [Unify.typeSymbol]?: any }> {
  Tag?: () => A[Unify.typeSymbol] extends Tag<infer I0, infer S0> | infer _ ? Tag<I0, S0> : never
}

Added in v2.0.0

TagUnifyIgnore (interface)

Signature

export interface TagUnifyIgnore {}

Added in v2.0.0

ValidTagsById (type alias)

Signature

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

Added in v2.0.0

symbol

TagTypeId (type alias)

Signature

export type TagTypeId = typeof TagTypeId

Added in v2.0.0

TypeId (type alias)

Signature

export type TypeId = typeof TypeId

Added in v2.0.0

unsafe

unsafeGet

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 {@link getOption}.

Signature

export 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 Context from "effect/Context"

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

Added in v2.0.0

utils

Tag (namespace)

Added in v2.0.0

Identifier (type alias)

Signature

export type Identifier<T extends Tag<any, any> | TagClassShape<any, any>> =
  T extends Tag<infer A, any> ? A : T extends TagClassShape<infer A, any> ? A : never

Added in v2.0.0

Service (type alias)

Signature

export type Service<T extends Tag<any, any> | TagClassShape<any, any>> =
  T extends Tag<any, infer A> ? A : T extends TagClassShape<any, infer A> ? A : never

Added in v2.0.0

add

Adds a service to a given Context.

Signature

export declare const add: {
  <T extends Tag<any, any>>(
    tag: T,
    service: Tag.Service<T>
  ): <Services>(self: Context<Services>) => Context<Services | Tag.Identifier<T>>
  <Services, T extends Tag<any, any>>(
    self: Context<Services>,
    tag: T,
    service: Tag.Service<T>
  ): Context<Services | Tag.Identifier<T>>
}

Example

import * as Context from "effect/Context"
import { pipe } from "effect/Function"

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

Added in v2.0.0

merge

Merges two Contexts, returning a new Context containing the services of both.

Signature

export 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 Context from "effect/Context"

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

Added in v2.0.0

omit

Signature

export declare const omit: <Services, S extends ValidTagsById<Services>[]>(
  ...tags: S
) => (self: Context<Services>) => Context<Exclude<Services, { [k in keyof S]: Tag.Identifier<S[k]> }[keyof S]>>

Added in v2.0.0

pick

Returns a new Context that contains only the specified services.

Signature

export declare const pick: <Services, S extends ValidTagsById<Services>[]>(
  ...tags: S
) => (self: Context<Services>) => Context<{ [k in keyof S]: Tag.Identifier<S[k]> }[number]>

Example

import * as Context from "effect/Context"
import { pipe } from "effect/Function"
import * as O from "effect/Option"

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), O.some({ PORT: 8080 }))
assert.deepStrictEqual(Context.getOption(Services, Timeout), O.none())

Added in v2.0.0