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

effect-core.mddocs/

Core Effect System

The heart of the Effect library - defines the fundamental Effect<A, E, R> type and core operations for creating, transforming, and executing effects with structured error handling and context management.

Capabilities

Effect Type

The core Effect<A, E, R> type represents computations that may succeed with value A, fail with error E, and require context R.

/**
 * The Effect type represents a computation that may succeed with a value of type A,
 * fail with an error of type E, or require a context of type R
 */
interface Effect<out A, out E = never, out R = never> extends Pipeable {
  readonly [EffectTypeId]: Effect.Variance<A, E, R>;
}

interface Variance<out A, out E, out R> {
  readonly _A: Covariant<A>;
  readonly _E: Covariant<E>;
  readonly _R: Covariant<R>;
}

Effect Creation

Functions for creating new Effect instances from values, errors, synchronous computations, and asynchronous operations.

/**
 * Creates an Effect that succeeds with the provided value
 */
function succeed<A>(value: A): Effect<A>;

/**
 * Creates an Effect that fails with the provided error
 */
function fail<E>(error: E): Effect<never, E>;

/**
 * Creates an Effect from a synchronous side-effecting function
 */
function sync<A>(thunk: LazyArg<A>): Effect<A>;

/**
 * Creates an Effect from an asynchronous operation using a callback
 */
function async<A, E = never, R = never>(
  register: (callback: (effect: Effect<A, E, R>) => void) => void
): Effect<A, E, R>;

/**
 * Creates an Effect that never completes
 */
function never: Effect<never>;

/**
 * Creates an Effect that succeeds with void
 */
function void: Effect<void>;

/**
 * Creates an Effect from a Promise
 */
function promise<A>(evaluate: LazyArg<Promise<A>>): Effect<A, never>;

/**
 * Creates an Effect from a Promise that may reject
 */
function tryPromise<A, E>(options: {
  readonly try: LazyArg<Promise<A>>;
  readonly catch: (unknown: unknown) => E;
}): Effect<A, E>;

Usage Examples:

import { Effect } from "effect";

// Success values
const successEffect = Effect.succeed(42);
const stringEffect = Effect.succeed("Hello World");

// Failures
const failureEffect = Effect.fail("Something went wrong");
const errorEffect = Effect.fail(new Error("Network error"));

// Synchronous effects
const syncEffect = Effect.sync(() => {
  console.log("Side effect!");
  return Math.random();
});

// Asynchronous effects
const asyncEffect = Effect.async<string, Error>((callback) => {
  setTimeout(() => {
    callback(Effect.succeed("Delayed result"));
  }, 1000);
});

// From promises
const promiseEffect = Effect.promise(() => fetch("/api/data"));
const safePromiseEffect = Effect.tryPromise({
  try: () => fetch("/api/data"),
  catch: (unknown) => `Network error: ${unknown}`
});

Effect Transformations

Functions for transforming Effect values while preserving error and context information.

/**
 * Transforms the success value of an Effect
 */
function map<A, B>(f: (a: A) => B): <E, R>(self: Effect<A, E, R>) => Effect<B, E, R>;

/**
 * Chains effects together, allowing the success value to determine the next effect
 */
function flatMap<A, B, E2, R2>(
  f: (a: A) => Effect<B, E2, R2>
): <E1, R1>(self: Effect<A, E1, R1>) => Effect<B, E1 | E2, R1 | R2>;

/**
 * Performs a side effect without changing the result
 */
function tap<A, X, E2, R2>(
  f: (a: A) => Effect<X, E2, R2>
): <E1, R1>(self: Effect<A, E1, R1>) => Effect<A, E1 | E2, R1 | R2>;

/**
 * Transforms both success and error values
 */
function mapBoth<E, E2, A, A2>(options: {
  readonly onFailure: (e: E) => E2;
  readonly onSuccess: (a: A) => A2;
}): <R>(self: Effect<A, E, R>) => Effect<A2, E2, R>;

/**
 * Transforms the error value of an Effect
 */
function mapError<E, E2>(f: (e: E) => E2): <A, R>(self: Effect<A, E, R>) => Effect<A, E2, R>;

/**
 * Flattens nested Effects
 */
function flatten<A, E1, R1, E2, R2>(
  self: Effect<Effect<A, E2, R2>, E1, R1>
): Effect<A, E1 | E2, R1 | R2>;

Usage Examples:

import { Effect, pipe } from "effect";

// Basic transformations
const doubled = pipe(
  Effect.succeed(21),
  Effect.map(x => x * 2)
); // Effect<number, never, never>

// Chaining effects
const program = pipe(
  Effect.succeed("input"),
  Effect.flatMap(input => 
    Effect.sync(() => input.toUpperCase())
  ),
  Effect.tap(result => 
    Effect.sync(() => console.log(`Result: ${result}`))
  )
); // Effect<string, never, never>

// Error transformation
const betterError = pipe(
  Effect.fail("generic error"),
  Effect.mapError(err => new Error(`Enhanced: ${err}`))
); // Effect<never, Error, never>

Error Handling

Comprehensive error handling capabilities for recovering from failures and managing error flow.

/**
 * Handles all errors by providing a recovery effect
 */
function catchAll<E, A2, E2, R2>(
  f: (e: E) => Effect<A2, E2, R2>
): <A, R>(self: Effect<A, E, R>) => Effect<A | A2, E2, R | R2>;

/**
 * Handles specific errors using a tag-based approach
 */
function catchTag<K extends E extends { _tag: string } ? E["_tag"] : never, E, A1, E1, R1>(
  k: K,
  f: (e: Extract<E, { _tag: K }>) => Effect<A1, E1, R1>
): <A, R>(self: Effect<A, E, R>) => Effect<A | A1, E1 | Exclude<E, { _tag: K }>, R | R1>;

/**
 * Handles some errors while preserving others
 */
function catchSome<E, A2, E2, R2>(
  pf: (e: E) => Option<Effect<A2, E2, R2>>
): <A, R>(self: Effect<A, E, R>) => Effect<A | A2, E | E2, R | R2>;

/**
 * Provides a fallback effect if the original fails
 */
function orElse<A2, E2, R2>(
  that: LazyArg<Effect<A2, E2, R2>>
): <A, E, R>(self: Effect<A, E, R>) => Effect<A | A2, E2, R | R2>;

/**
 * Handles defects (unexpected errors) that would normally crash the program
 */
function catchAllDefect<A2, E2, R2>(
  f: (defect: unknown) => Effect<A2, E2, R2>
): <A, E, R>(self: Effect<A, E, R>) => Effect<A | A2, E | E2, R | R2>;

/**
 * Ensures an effect runs regardless of success or failure
 */
function ensuring<X, E2, R2>(
  finalizer: Effect<X, E2, R2>
): <A, E, R>(self: Effect<A, E, R>) => Effect<A, E | E2, R | R2>;

/**
 * Retries an effect according to a schedule
 */
function retry<S, R1, O>(
  policy: Schedule<S, any, O>
): <A, E, R>(self: Effect<A, E, R>) => Effect<A, E, R | R1>;

Usage Examples:

import { Effect, pipe } from "effect";

// Basic error recovery
const recovered = pipe(
  Effect.fail("Network timeout"),
  Effect.catchAll(error => Effect.succeed(`Fallback for: ${error}`))
);

// Selective error handling
const selective = pipe(
  riskyOperation,
  Effect.catchTag("NetworkError", error => 
    Effect.succeed(`Retrying after network error: ${error.message}`)
  ),
  Effect.catchTag("ValidationError", error =>
    Effect.fail(`Invalid input: ${error.field}`)
  )
);

// Fallback chain
const withFallback = pipe(
  primaryService,
  Effect.orElse(() => backupService),
  Effect.orElse(() => Effect.succeed("default value"))
);

// Cleanup guarantee
const safeOperation = pipe(
  acquireResource,
  Effect.flatMap(resource => doWork(resource)),
  Effect.ensuring(releaseResource)
);

Effect Execution

Functions for running Effect computations and extracting their results.

/**
 * Runs an Effect and returns a Promise of the result
 */
function runPromise<A, E>(effect: Effect<A, E>): Promise<A>;

/**
 * Runs an Effect synchronously and returns the result
 * Throws if the effect is asynchronous or fails
 */
function runSync<A, E>(effect: Effect<A, E>): A;

/**
 * Runs an Effect and returns an Exit value representing success or failure
 */
function runPromiseExit<A, E>(effect: Effect<A, E>): Promise<Exit<A, E>>;

/**
 * Runs an Effect synchronously and returns an Exit value
 */
function runSyncExit<A, E>(effect: Effect<A, E>): Exit<A, E>;

/**
 * Runs an Effect with a callback for the result
 */
function runCallback<A, E>(
  effect: Effect<A, E>
): (callback?: (exit: Exit<A, E>) => void) => (fiberId?: FiberId) => void;

Usage Examples:

import { Effect } from "effect";

// Promise-based execution
const result = await Effect.runPromise(
  Effect.succeed(42)
); // 42

// Synchronous execution
const syncResult = Effect.runSync(
  Effect.succeed("Hello")
); // "Hello"

// Safe execution with Exit
const exit = await Effect.runPromiseExit(
  Effect.fail("Something went wrong")
);
if (exit._tag === "Success") {
  console.log(exit.value);
} else {
  console.error(exit.cause);
}

// Error handling in promises
try {
  await Effect.runPromise(Effect.fail("Error"));
} catch (error) {
  console.error("Effect failed:", error);
}

Context Operations

Functions for working with Effect context requirements for dependency injection.

/**
 * 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 a complete context to an Effect
 */
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 context 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 { Effect, Context } from "effect";

// Define a service
interface Logger {
  log(message: string): Effect<void>;
}
const Logger = Context.GenericTag<Logger>("Logger");

// Use the service
const program = Effect.gen(function* () {
  const logger = yield* Logger;
  yield* logger.log("Hello from program");
  return "done";
});

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

const runnable = pipe(
  program,
  Effect.provideService(Logger, logger)
);

await Effect.runPromise(runnable);

Generator Syntax

Effect provides generator-based syntax for writing imperative-style code with effects.

/**
 * Creates an Effect from a generator function
 */
function gen<Eff extends YieldWrap<Effect<any, any, any>>, AEff>(
  f: (resume: Adapter) => Generator<Eff, AEff, any>
): Effect<AEff, Effect.Error<Eff>, Effect.Context<Eff>>;

/**
 * Do notation for Effect computations
 */
const Do: Effect<{}>;

/**
 * Bind a value in Do notation
 */
function bind<N extends string, A, E2, R2>(
  name: Exclude<N, keyof A>,
  f: (a: A) => Effect<N, E2, R2>
): <E1, R1>(self: Effect<A, E1, R1>) => Effect<A & { [K in N]: N }, E1 | E2, R1 | R2>;

Usage Examples:

import { Effect } from "effect";

// Generator syntax
const program = Effect.gen(function* () {
  const x = yield* Effect.succeed(10);
  const y = yield* Effect.succeed(20);
  const sum = x + y;
  yield* Effect.sync(() => console.log(`Sum: ${sum}`));
  return sum;
});

// Do notation
const doProgram = pipe(
  Effect.Do,
  Effect.bind("x", () => Effect.succeed(10)),
  Effect.bind("y", () => Effect.succeed(20)),
  Effect.bind("sum", ({ x, y }) => Effect.succeed(x + y)),
  Effect.tap(({ sum }) => Effect.sync(() => console.log(`Sum: ${sum}`))),
  Effect.map(({ sum }) => sum)
);

Types

// Lazy evaluation type
type LazyArg<A> = () => A;

// Effect variance markers
interface Covariant<T> {
  readonly _T: (_: never) => T;
}

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

// Exit type for effect results
type Exit<A, E = never> = Exit.Success<A> | Exit.Failure<E>;

declare namespace Exit {
  interface Success<A> {
    readonly _tag: "Success";
    readonly value: A;
  }
  
  interface Failure<E> {
    readonly _tag: "Failure";
    readonly cause: Cause<E>;
  }
}

// Fiber ID for tracking fibers
interface FiberId {
  readonly _tag: "FiberId";
  readonly id: number;
  readonly startTimeMillis: number;
}