2.4 KiB
2.4 KiB
Effect Library Development Patterns
NEVER: try-catch in Effect.gen
REASON: Effect generators handle errors through the Effect type system, not JavaScript exceptions.
// ❌ WRONG - This will cause runtime errors
Effect.gen(function*() {
try {
const result = yield* someEffect
return result
} catch (error) {
// This will never be reached and breaks Effect semantics
console.error(error)
}
})
// ✅ CORRECT - Use Effect's built-in error handling
Effect.gen(function*() {
const result = yield* Effect.result(someEffect)
if (result._tag === "Failure") {
// Handle error case properly
console.error("Effect failed:", result.cause)
return yield* Effect.fail("Handled error")
}
return result.value
})
return yield* Pattern for Errors
CRITICAL: Always use return yield* when yielding terminal effects.
// ✅ CORRECT - Makes termination explicit
Effect.gen(function*() {
if (invalidCondition) {
return yield* Effect.fail("Validation failed")
}
if (shouldInterrupt) {
return yield* Effect.interrupt
}
// Continue with normal flow
const result = yield* someOtherEffect
return result
})
// ❌ WRONG - Missing return keyword leads to unreachable code
Effect.gen(function*() {
if (invalidCondition) {
yield* Effect.fail("Validation failed") // Missing return!
// Unreachable code after error!
}
})
Effect.gen and Effect.fnUntraced
Prefer Effect.fnUntraced over functions that only return Effect.gen.
// ❌ AVOID - Function only wraps Effect.gen
const fn = (param: string) =>
Effect.gen(function*() {
// ...
})
// ✅ PREFER - Reusable untraced Effect function
const fn = Effect.fnUntraced(function*(param: string) {
// ...
})
When to Use What
Use Effect.gen when:
- Writing inline effect composition
- One-off operations that don't need to be reused
- Inside other functions already being traced
Use Effect.fnUntraced when:
- Building library implementations
- Performance is critical (hot paths)
- Function is called many times per operation
- Tracing overhead is unacceptable
Context.Service
Prefer the class syntax when working with Context.Service.
import { Context } from "effect"
class MyService extends Context.Service<MyService, {
readonly doSomething: (input: string) => number
}>()("MyService") {}