Skip to main content
Effect provides several constructors to create Effect values from different kinds of computations.

succeed - Pure Success Values

Creates an effect that always succeeds with a given value:
export const succeed: <A>(value: A) => Effect<A>
1
Basic Usage
2
import { Effect } from "effect"

// Effect<number, never, never>
const success = Effect.succeed(42)

Effect.runSync(success)  // Returns: 42
3
Use Cases
4
  • Lifting pure values into the Effect context
  • Providing fallback values in error recovery
  • Creating constant effects for testing
  • fail - Typed Errors

    Creates an effect that fails with a typed error:
    export const fail: <E>(error: E) => Effect<never, E>
    
    import { Effect } from "effect"
    
    class NetworkError {
      readonly _tag = "NetworkError"
      constructor(readonly message: string) {}
    }
    
    // Effect<never, NetworkError, never>
    const failure = Effect.fail(
      new NetworkError("Connection timeout")
    )
    
    Effect.runPromise(failure)  // Rejects with NetworkError
    
    Use fail for expected, recoverable errors. The error type is tracked in the Effect’s type signature, enabling type-safe error handling.

    sync - Lazy Synchronous Computation

    Wraps a synchronous side-effecting function:
    export const sync: <A>(thunk: LazyArg<A>) => Effect<A>
    
    import { Effect } from "effect"
    
    const program = Effect.sync(() => {
      console.log("This is a side effect!")
      return Math.random()
    })
    // Effect<number, never, never>
    
    // The function isn't called until the effect runs
    Effect.runSync(program)  // NOW the side effect executes
    
    1
    When to Use sync
    2
    Use sync when:
    3
  • You have synchronous side effects (I/O, logging, mutations)
  • The computation should be deferred until the effect runs
  • You want effects to be re-executable with fresh values
  • 4
    Don’t use sync when:
    5
  • You have a pure value (use succeed instead)
  • The computation is asynchronous (use async instead)
  • The computation might throw (use try or trySync instead)
  • 6
    Lazy Evaluation
    7
    import { Effect } from "effect"
    
    let counter = 0
    
    const increment = Effect.sync(() => {
      counter++
      return counter
    })
    
    // Each run executes the thunk again
    Effect.runSync(increment)  // 1
    Effect.runSync(increment)  // 2
    Effect.runSync(increment)  // 3
    

    async - Asynchronous Effects

    Wraps callback-based or promise-based async operations:
    export const async: <A, E = never, R = never>(
      resume: (callback: (_: Effect<A, E, R>) => void, signal: AbortSignal) => void | Effect<void, never, R>,
      blockingOn?: FiberId.FiberId
    ) => Effect<A, E, R>
    

    Callback-Based APIs

    import { Effect } from "effect"
    import * as fs from "fs"
    
    const readFile = (path: string): Effect.Effect<string, Error> =>
      Effect.async<string, Error>((resume) => {
        fs.readFile(path, "utf-8", (error, data) => {
          if (error) {
            resume(Effect.fail(error))
          } else {
            resume(Effect.succeed(data))
          }
        })
      })
    

    Promise-Based APIs

    For promises, Effect provides a specialized constructor:
    import { Effect } from "effect"
    
    // Simple promise wrapping
    const fetchUser = (id: number) =>
      Effect.promise(() => 
        fetch(`/api/users/${id}`).then(r => r.json())
      )
    
    // With typed error handling
    class FetchError {
      readonly _tag = "FetchError"
      constructor(readonly cause: unknown) {}
    }
    
    const fetchUserSafe = (id: number) =>
      Effect.tryPromise({
        try: () => fetch(`/api/users/${id}`).then(r => r.json()),
        catch: (error) => new FetchError(error)
      })
    

    Cancellation Support

    The async constructor provides an AbortSignal for cancellation:
    import { Effect } from "effect"
    
    const delayed = (ms: number): Effect.Effect<void> =>
      Effect.async<void>((resume, signal) => {
        const timeoutId = setTimeout(() => {
          resume(Effect.void)
        }, ms)
    
        // Cleanup function - called if the effect is interrupted
        signal.addEventListener("abort", () => {
          clearTimeout(timeoutId)
        })
      })
    
    const program = Effect.gen(function* () {
      yield* delayed(5000)  // Will be cancelled after 1 second
    })
    
    Effect.runPromise(
      program.pipe(Effect.timeout("1 second"))
    )
    

    Other Constructors

    void - No-op Effect

    import { Effect } from "effect"
    
    // Effect<void, never, never>
    const noop = Effect.void
    

    succeedNone / succeedSome

    import { Effect } from "effect"
    
    // Effect<Option<never>, never, never>
    const none = Effect.succeedNone
    
    // Effect<Option<number>, never, never>
    const some = Effect.succeedSome(42)
    

    suspend - Lazy Effect Creation

    Delays the creation of an Effect until it’s needed:
    import { Effect } from "effect"
    
    const program = Effect.suspend(() => {
      console.log("Creating effect...")
      return Effect.succeed(42)
    })
    
    // "Creating effect..." is only logged when the effect runs
    Effect.runSync(program)
    
    Use suspend for recursive effects or when you need to capture variables at execution time, not definition time.

    Comparison Table

    ConstructorWhen to UseExample
    succeedPure valueEffect.succeed(42)
    failTyped errorEffect.fail(new Error())
    syncSync side effectEffect.sync(() => console.log())
    asyncAsync callbackEffect.async((resume) => ...)
    promisePromise wrappingEffect.promise(() => fetch())
    tryPromisePromise with typed errorsEffect.tryPromise({ try, catch })
    suspendLazy effect creationEffect.suspend(() => effect)

    Best Practices

    1
    Choose the Right Constructor
    2
    Use the most specific constructor for your use case:
    3
  • Pure values → succeed
  • Side effects → sync
  • Async operations → async or promise
  • Expected failures → fail
  • 4
    Type Your Errors
    5
    Always use structured error types with a _tag field:
    6
    import { Effect } from "effect"
    
    class ValidationError {
      readonly _tag = "ValidationError"
      constructor(readonly field: string) {}
    }
    
    class DatabaseError {
      readonly _tag = "DatabaseError"
      constructor(readonly message: string) {}
    }
    
    const program: Effect.Effect<User, ValidationError | DatabaseError> = ...
    
    7
    Defer Side Effects
    8
    Always wrap side effects in sync or async, never execute them directly:
    9
    import { Effect } from "effect"
    
    // ❌ Bad: Side effect executes immediately
    const bad = Effect.succeed(console.log("Hello"))
    
    // ✅ Good: Side effect deferred until effect runs
    const good = Effect.sync(() => console.log("Hello"))
    

    Build docs developers (and LLMs) love