Context.empty (22)
, Context.add (20)
and Context.get (21)
are enough to do all the main operations on a context. There are many other helper functions. Most of them rely on these three builders.
Context.make creates a context with one item. That item might be using a Context.GenericTag (19)
import { Context } from "effect";
const MinimumScore = Context.GenericTag<"MinimumScore", number>( "@fred.c/MinimumScore",);
const scoreContext = Context.make(MinimumScore, 3);
// ...laterconst three = Context.get(scoreContext, MinimumScore);Or it might be using a Context.Tag (16)
class DiceService extends Context.Tag("@fred.c/DiceService")< DiceService, { roll: () => number }>() {}
const diceContext = Context.make(DiceService, { roll: () => Math.ceil(Math.random() * 6),});
// ...laterconst diceRoll = Context.get(diceContext, DiceService).roll();Note that we could implement this as
const make = <T extends Context.Tag<any, any>>( tag: T, service: Context.Tag.Service<T>,): Context.Context<Context.Tag.Identifier<T>> => Context.empty().pipe(Context.add(tag, service));
// usage:const scoreContext = make(MinimumScore, 2);Behind the scenes this is implemented by constructing a Map directly.
Here is the implementation:
const ContextProto: Omit<C.Context<unknown>, "unsafeMap"> = { // defines a few methods, e.g. `pipe` and `toString`, // that should be available on every Context // elided here for brevity}
const makeContext = <Services>(unsafeMap: Map<string, any>): C.Context<Services> => { const context = Object.create(ContextProto) context.unsafeMap = unsafeMap return context}
const make = <T extends C.Tag<any, any>>( tag: T, service: C.Tag.Service<T>): C.Context<C.Tag.Identifier<T>> => makeContext(new Map([[tag.key, service]]))The Effect source code can be intimidating at first. Most functions are quite short and simple, but it can be hard to unpick how the pieces fit together.
Making a Context with one service is so common that it is exposed directly as a method on tags. Effect does not expose many instance methods, but in this case it is convenient.
const sixContext = MinimumScore.context(6)const lowRollContext = DiceService.context({ roll: () => 1})Behind the scenes this called Context.make.
Let’s recap
- A Context maps tags to service implementations
- Operations on Context are plain (non-effectful) operations
- There are multiple equivalent ways to make a Context with one service
- Use whichever is most convenient for your current situation
- Effect optimises some operations behind the scenes, using internal implementation details