or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

concurrency-fibers.mdcontext-services.mddata-structures.mdeffect-core.mderror-observability.mdfunction-utilities.mdindex.mdlayer-system.mdschema-validation.mdstreaming.md
tile.json

context-services.mddocs/

Context & Dependency Injection

Type-safe service management and dependency injection system using Context and Tag for organizing and providing services throughout your application with complete compile-time safety.

Capabilities

Context System

The Context system provides a type-safe way to manage services and dependencies.

/**
 * A Context is a collection of services
 */
interface Context<in Services> {}

/**
 * A Tag identifies a specific service type
 */
interface Tag<in in out Self, in Type> {
  readonly _tag: "Tag";
  readonly identifier: string;
}

declare namespace Context {
  /**
   * Creates an empty context
   */
  function empty(): Context<never>;
  
  /**
   * Creates a context with services
   */
  function make<Services>(): Context<Services>;
  
  /**
   * Adds a service to the context
   */
  function add<T>(tag: Tag<T, T>, service: T): <Services>(self: Context<Services>) => Context<Services | T>;
  
  /**
   * Gets a service from the context
   */
  function get<Services, T>(tag: Tag<T, T>): (self: Context<Services>) => T;
  
  /**
   * Checks if a service exists in the context
   */
  function has<T>(tag: Tag<T, T>): <Services>(self: Context<Services>) => boolean;
  
  /**
   * Removes a service from the context
   */
  function omit<T>(tag: Tag<T, T>): <Services>(self: Context<Services>) => Context<Exclude<Services, T>>;
  
  /**
   * Merges two contexts
   */
  function merge<Services1, Services2>(that: Context<Services2>): (self: Context<Services1>) => Context<Services1 | Services2>;
  
  /**
   * Picks specific services from a context
   */
  function pick<Services, S extends Services>(...tags: Tag<S, S>[]): (self: Context<Services>) => Context<S>;
}

Service Creation

Functions for creating and managing service tags and implementations.

/**
 * Creates a generic service tag
 */
function GenericTag<Self, Type = Self>(identifier: string): Tag<Self, Type>;

/**
 * Creates a service tag with validation
 */
function Tag<Self, Type = Self>(identifier: string): Tag<Self, Type>;

Usage Examples:

import { Context, Effect } from "effect";

// Define services
interface Logger {
  log(message: string): Effect<void>;
}

interface Database {
  query(sql: string): Effect<unknown[]>;
}

// Create service tags
const Logger = Context.GenericTag<Logger>("Logger");
const Database = Context.GenericTag<Database>("Database");

// Create service implementations  
const logger: Logger = {
  log: (message) => Effect.sync(() => console.log(message))
};

const database: Database = {
  query: (sql) => Effect.sync(() => [{ id: 1, name: "test" }])
};

// Build context
const context = pipe(
  Context.empty(),
  Context.add(Logger, logger),
  Context.add(Database, database)
);

// Use services in effects
const program = Effect.gen(function* () {
  const logger = yield* Logger;
  const database = yield* Database;
  
  yield* logger.log("Starting query");
  const results = yield* database.query("SELECT * FROM users");
  yield* logger.log(`Found ${results.length} users`);
  
  return results;
});

// Run with context
const result = await Effect.runPromise(
  pipe(program, Effect.provideContext(context))
);

Effect Context Operations

Functions for working with services in Effect computations.

declare namespace Effect {
  /**
   * Access a service from the context
   */
  function service<T>(tag: Tag<T, T>): Effect<T, never, T>;
  
  /**
   * Provide a service to an Effect
   */
  function provideService<T>(tag: Tag<T, T>, service: T): <A, E, R>(self: Effect<A, E, R>) => Effect<A, E, Exclude<R, T>>;
  
  /**
   * Provide multiple services at once
   */
  function provideContext<R>(context: Context<R>): <A, E>(self: Effect<A, E, R>) => Effect<A, E>;
  
  /**
   * Access the entire context
   */
  function context<R>(): Effect<Context<R>, never, R>;
  
  /**
   * Provide a service using an Effect
   */
  function provideServiceEffect<T, E, R>(
    tag: Tag<T, T>,
    effect: Effect<T, E, R>
  ): <A, E1, R1>(self: Effect<A, E1, R1 | T>) => Effect<A, E | E1, R | Exclude<R1, T>>;
}

Usage Examples:

import { Context, Effect, pipe } from "effect";

// Service-based architecture
interface ApiClient {
  get(url: string): Effect<unknown, Error>;
  post(url: string, data: unknown): Effect<unknown, Error>;
}

interface UserService {
  getUser(id: string): Effect<User, Error>;
  createUser(data: CreateUserData): Effect<User, Error>;
}

const ApiClient = Context.GenericTag<ApiClient>("ApiClient");
const UserService = Context.GenericTag<UserService>("UserService");

// Implementation that depends on other services
const userService: UserService = {
  getUser: (id) => Effect.gen(function* () {
    const client = yield* ApiClient;
    const user = yield* client.get(`/users/${id}`);
    return user as User;
  }),
  
  createUser: (data) => Effect.gen(function* () {
    const client = yield* ApiClient;
    const user = yield* client.post("/users", data);
    return user as User;
  })
};

// Usage in application
const app = Effect.gen(function* () {
  const userSvc = yield* UserService;
  const user = yield* userSvc.getUser("123");
  yield* Effect.log(`Retrieved user: ${user.name}`);
  return user;
});

Types

// Service identifier type
type ServiceIdentifier = string;

// Context variance
interface Context<in Services> {
  readonly [ContextTypeId]: {
    readonly _Services: Contravariant<Services>;
  };
}

// Tag variance  
interface Tag<in in out Self, in Type> {
  readonly [TagTypeId]: {
    readonly _Self: Invariant<Self>;
    readonly _Type: Contravariant<Type>;
  };
}

// Variance markers
interface Contravariant<T> {
  readonly _T: (_: T) => void;
}

interface Invariant<T> {
  readonly _T: {
    readonly _A: (_: never) => T;
    readonly _B: (_: T) => void;
  };
}