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 Tag
s to their implementations (called Service
s), 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 { Context } from "effect"
assert.strictEqual(Context.GenericTag("PORT").key === Context.GenericTag("PORT").key, true)
Added in v2.0.0
Reference
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
export declare const Reference: <Self>() => <const Id extends string, Service>(
id: Id,
options: { readonly defaultValue: () => Service }
) => ReferenceClass<Self, Id, Service>
Example
// Title: Declaring a Tag with a default value
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
// Title: 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
Added in v3.11.0
Tag
Signature
export declare const Tag: <const Id extends string>(id: Id) => <Self, Shape>() => TagClass<Self, Id, Shape>
Example
import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<MyTag, { readonly myNum: number }>() {
static Live = Layer.succeed(this, { myNum: 108 })
}
Added in v2.0.0
empty
Returns an empty Context
.
Signature
export declare const empty: () => Context<never>
Example
import { Context } from "effect"
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 { 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 })
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: {
<I, S>(tag: Reference<I, S>): <Services>(self: Context<Services>) => S
<Services, T extends ValidTagsById<Services>>(tag: T): (self: Context<Services>) => Tag.Service<T>
<Services, I, S>(self: Context<Services>, tag: Reference<I, S>): S
<Services, T extends ValidTagsById<Services>>(self: Context<Services>, tag: T): Tag.Service<T>
}
Example
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 })
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 { 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())
Added in v2.0.0
getOrElse
Get a service from the context that corresponds to the given tag, or use the fallback value.
Signature
export 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
}
Added in v3.7.0
guards
isContext
Checks if the provided argument is a Context
.
Signature
export declare const isContext: (input: unknown) => input is Context<never>
Example
import { Context } from "effect"
assert.strictEqual(Context.isContext(Context.empty()), true)
Added in v2.0.0
isReference
Checks if the provided argument is a Reference
.
Signature
export declare const isReference: (u: unknown) => u is Reference<any, any>
Added in v3.11.0
isTag
Checks if the provided argument is a Tag
.
Signature
export declare const isTag: (input: unknown) => input is Tag<any, any>
Example
import { Context } from "effect"
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
Reference (interface)
Signature
export interface Reference<in out Id, in out Value> extends Pipeable, Inspectable {
readonly [ReferenceTypeId]: ReferenceTypeId
readonly defaultValue: () => Value
readonly _op: "Tag"
readonly Service: Value
readonly Identifier: Id
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 v3.11.0
ReferenceClass (interface)
Signature
export interface ReferenceClass<Self, Id extends string, Type> extends Reference<Self, Type> {
new (_: never): TagClassShape<Id, Type>
readonly key: Id
}
Added in v3.11.0
Tag (interface)
Signature
export interface Tag<in out Id, in out Value> extends Pipeable, Inspectable {
readonly _op: "Tag"
readonly Service: Value
readonly Identifier: Id
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 v3.5.9
TagClass (interface)
Signature
export interface TagClass<Self, Id extends string, Type> extends Tag<Self, Type> {
new (_: never): TagClassShape<Id, Type>
readonly key: Id
}
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
ReferenceTypeId (type alias)
Signature
export type ReferenceTypeId = typeof ReferenceTypeId
Added in v3.11.0
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 { 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))
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<any, any> ? T["Identifier"] : T extends TagClassShape<any, any> ? T : 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, any> ? T["Service"] : 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 { 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 })
Added in v2.0.0
merge
Merges two Context
s, 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 { 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 })
Added in v2.0.0
mergeAll
Merges any number of Context
s, returning a new Context
containing the services of all.
Signature
export declare const mergeAll: <T extends Array<unknown>>(
...ctxs_0: { [K in keyof T]: Context<T[K]> }
) => Context<T[number]>
Example
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" })
Added in v3.12.0
omit
Signature
export declare const omit: <Services, S extends Array<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 Array<ValidTagsById<Services>>>(
...tags: S
) => (self: Context<Services>) => Context<{ [k in keyof S]: Tag.Identifier<S[k]> }[number]>
Example
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())
Added in v2.0.0