Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    ojowwalker77

    effect-ts-expert

    ojowwalker77/effect-ts-expert
    Coding
    86
    21 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    This skill should be used when the user is working with Effect-TS, asks to "write Effect code", "use Effect", "functional TypeScript", "handle errors with Effect", "dependency injection Effect",...

    SKILL.md

    Effect-TS Expert

    Expert-level guidance for Effect-TS functional programming with typed errors, dependency injection, concurrency, and production-ready patterns.

    Core Concepts

    The Effect Type

    Effect<Success, Error, Requirements>
    //     ^        ^       ^
    //     |        |       └── Services/dependencies needed (Context)
    //     |        └────────── Typed error channel
    //     └─────────────────── Success value type
    

    Key insight: Effects are lazy descriptions of computations. They don't execute until run.

    Creating Effects

    import { Effect } from "effect"
    
    // From pure values
    const success = Effect.succeed(42)
    const failure = Effect.fail(new Error("oops"))
    
    // From sync code (may throw)
    const sync = Effect.sync(() => JSON.parse(data))
    const trySync = Effect.try({
      try: () => JSON.parse(data),
      catch: (e) => new ParseError(e)
    })
    
    // From async code
    const promise = Effect.promise(() => fetch(url))
    const tryPromise = Effect.tryPromise({
      try: () => fetch(url).then(r => r.json()),
      catch: (e) => new FetchError(e)
    })
    
    // From callbacks
    const callback = Effect.async<string, Error>((resume) => {
      someCallbackApi((err, result) => {
        if (err) resume(Effect.fail(err))
        else resume(Effect.succeed(result))
      })
    })
    

    Running Effects

    // Development/testing
    Effect.runSync(effect)           // Sync, throws on async/error
    Effect.runPromise(effect)        // Returns Promise<A>
    Effect.runPromiseExit(effect)    // Returns Promise<Exit<A, E>>
    
    // Production (with runtime)
    const runtime = ManagedRuntime.make(AppLayer)
    await runtime.runPromise(effect)
    

    Building Pipelines

    pipe and Effect.gen

    import { Effect, pipe } from "effect"
    
    // Using pipe (point-free style)
    const program = pipe(
      Effect.succeed(5),
      Effect.map(n => n * 2),
      Effect.flatMap(n => n > 5
        ? Effect.succeed(n)
        : Effect.fail(new Error("too small"))
      ),
      Effect.tap(n => Effect.log(`Result: ${n}`))
    )
    
    // Using Effect.gen (generator style - RECOMMENDED)
    const program = Effect.gen(function* () {
      const n = yield* Effect.succeed(5)
      const doubled = n * 2
      if (doubled <= 5) {
        return yield* Effect.fail(new Error("too small"))
      }
      yield* Effect.log(`Result: ${doubled}`)
      return doubled
    })
    

    Recommendation: Prefer Effect.gen for readability. Use pipe for simple transformations.

    Error Handling

    Typed Errors vs Defects

    Type Use Case Recovery
    Typed Error Domain failures (validation, not found, permissions) Yes - caller can handle
    Defect Bugs, invariant violations, unrecoverable No - terminates fiber
    // Typed errors - tracked in type system
    class NotFoundError extends Data.TaggedError("NotFoundError")<{
      readonly id: string
    }> {}
    
    class ValidationError extends Data.TaggedError("ValidationError")<{
      readonly message: string
    }> {}
    
    const findUser = (id: string): Effect.Effect<User, NotFoundError> =>
      pipe(
        db.query(id),
        Effect.flatMap(user =>
          user ? Effect.succeed(user) : Effect.fail(new NotFoundError({ id }))
        )
      )
    
    // Defects - for bugs, not domain errors
    const divide = (a: number, b: number): Effect.Effect<number> =>
      b === 0
        ? Effect.die(new Error("Division by zero - this is a bug!"))
        : Effect.succeed(a / b)
    

    Error Recovery

    // Catch all errors
    Effect.catchAll(effect, (error) => Effect.succeed(fallback))
    
    // Catch specific tagged errors
    Effect.catchTag(effect, "NotFoundError", (e) =>
      Effect.succeed(defaultUser)
    )
    
    // Catch multiple tags
    Effect.catchTags(effect, {
      NotFoundError: (e) => Effect.succeed(defaultUser),
      ValidationError: (e) => Effect.fail(new HttpError(400, e.message))
    })
    
    // Convert to Either (errors become Left)
    Effect.either(effect)  // Effect<Either<E, A>, never, R>
    
    // Retry on failure
    Effect.retry(effect, Schedule.recurs(3))
    

    Best Practice: Error Design

    // DO: Use tagged errors with Schema
    import { Schema } from "effect"
    
    class ApiError extends Schema.TaggedError<ApiError>()("ApiError", {
      status: Schema.Number,
      message: Schema.String,
    }) {}
    
    // DON'T: Use plain Error or strings
    Effect.fail(new Error("something went wrong"))  // Loses type info
    Effect.fail("error")  // Not an Error type
    

    Dependency Injection

    Services with Context.Tag

    import { Context, Effect, Layer } from "effect"
    
    // 1. Define service interface
    class UserRepository extends Context.Tag("UserRepository")<
      UserRepository,
      {
        readonly findById: (id: string) => Effect.Effect<User, NotFoundError>
        readonly save: (user: User) => Effect.Effect<void>
      }
    >() {}
    
    // 2. Use in effects
    const getUser = (id: string) => Effect.gen(function* () {
      const repo = yield* UserRepository
      return yield* repo.findById(id)
    })
    // Type: Effect<User, NotFoundError, UserRepository>
    
    // 3. Create layer implementation
    const UserRepositoryLive = Layer.succeed(UserRepository, {
      findById: (id) => Effect.tryPromise(() => db.users.find(id)),
      save: (user) => Effect.tryPromise(() => db.users.save(user))
    })
    
    // 4. Provide to run
    const program = getUser("123")
    const runnable = Effect.provide(program, UserRepositoryLive)
    

    Effect.Service (Simplified Pattern)

    // Combines Tag + Layer in one declaration
    class Logger extends Effect.Service<Logger>()("Logger", {
      // Option 1: Sync implementation
      sync: () => ({
        log: (msg: string) => console.log(msg)
      }),
    
      // Option 2: Effect-based with dependencies
      effect: Effect.gen(function* () {
        const config = yield* Config
        return {
          log: (msg: string) => Effect.sync(() =>
            console.log(`[${config.level}] ${msg}`)
          )
        }
      }),
      dependencies: [ConfigLive]
    }) {}
    
    // Use directly
    const program = Logger.log("Hello")
    
    // Access via Layer
    Effect.provide(program, Logger.Default)
    

    Layer Composition

    // Merge independent layers
    const BaseLayer = Layer.merge(ConfigLive, LoggerLive)
    
    // Provide dependencies
    const DbLayer = Layer.provide(DatabaseLive, ConfigLive)
    
    // Full composition
    const AppLayer = pipe(
      Layer.merge(ConfigLive, LoggerLive),
      Layer.provideMerge(DatabaseLive),
      Layer.provideMerge(UserRepositoryLive)
    )
    

    See references/layers.md for advanced patterns.

    Concurrency

    Fibers

    // Fork to run concurrently
    const fiber = yield* Effect.fork(longRunningTask)
    
    // Wait for result
    const result = yield* Fiber.join(fiber)
    
    // Interrupt
    yield* Fiber.interrupt(fiber)
    
    // Race - first to complete wins
    const fastest = yield* Effect.race(task1, task2)
    
    // All - run all, collect results
    const results = yield* Effect.all([task1, task2, task3])
    
    // All with concurrency limit
    const results = yield* Effect.all(tasks, { concurrency: 5 })
    

    Synchronization Primitives

    // Ref - mutable reference
    const counter = yield* Ref.make(0)
    yield* Ref.update(counter, n => n + 1)
    const value = yield* Ref.get(counter)
    
    // Queue - bounded producer/consumer
    const queue = yield* Queue.bounded<number>(100)
    yield* Queue.offer(queue, 42)
    const item = yield* Queue.take(queue)
    
    // Semaphore - limit concurrent access
    const sem = yield* Effect.makeSemaphore(3)
    yield* sem.withPermits(1)(expensiveOperation)
    
    // Deferred - one-shot signal
    const deferred = yield* Deferred.make<string, Error>()
    yield* Deferred.succeed(deferred, "done")
    const value = yield* Deferred.await(deferred)
    

    Resource Management

    Scoped Resources

    // Acquire/release pattern
    const file = Effect.acquireRelease(
      Effect.sync(() => fs.openSync(path, "r")),  // acquire
      (fd) => Effect.sync(() => fs.closeSync(fd))  // release
    )
    
    // Use with scoped
    const program = Effect.scoped(
      Effect.gen(function* () {
        const fd = yield* file
        return yield* readFile(fd)
      })
    )
    // File automatically closed after scope
    

    Finalizers

    const program = Effect.gen(function* () {
      yield* Effect.addFinalizer((exit) =>
        Effect.log(`Cleanup: ${exit._tag}`)
      )
      // ... do work
    })
    
    const runnable = Effect.scoped(program)
    

    Quick Reference

    Common Operators

    Operator Purpose
    Effect.map Transform success value
    Effect.flatMap Chain effects (monadic bind)
    Effect.tap Side effect, keep original value
    Effect.andThen Sequence, can be value or effect
    Effect.catchAll Handle all errors
    Effect.catchTag Handle specific tagged error
    Effect.provide Inject dependencies
    Effect.retry Retry with schedule
    Effect.timeout Add timeout
    Effect.fork Run concurrently
    Effect.all Parallel execution

    When to Use What

    Scenario Use
    Transform value Effect.map
    Chain effects Effect.flatMap or Effect.gen
    Error recovery Effect.catchTag / Effect.catchAll
    Add logging Effect.tap + Effect.log
    Run in parallel Effect.all with concurrency
    Limit concurrency Semaphore
    Share mutable state Ref
    Producer/consumer Queue
    One-time signal Deferred
    Cleanup resources Effect.acquireRelease

    Reference Documents

    • references/error-handling.md - Typed errors, defects, recovery patterns
    • references/layers.md - Dependency injection, service composition
    • references/concurrency.md - Fibers, synchronization, parallelism
    • references/streams.md - Stream, Sink, Channel patterns
    • references/schema.md - Validation, encoding/decoding
    • references/testing.md - Test layers, mocking, vitest integration
    • references/config.md - Configuration management
    • references/anti-patterns.md - Common mistakes and fixes
    • references/fp-ts-migration.md - Migration from fp-ts

    Usage

    This skill activates automatically when working with Effect-TS files or when the user mentions Effect, functional TypeScript, or typed errors.

    Explicit invocation:

    /effect-ts help me refactor this to use Effect
    /effect-ts create a service with dependency injection
    /effect-ts fix error handling in this code
    
    Recommended Servers
    Vercel Grep
    Vercel Grep
    Svelte
    Svelte
    Repository
    ojowwalker77/claude-matrix
    Files