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.
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>;
}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}`
});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>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)
);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);
}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);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)
);// 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;
}