prefer-effect-fn
Intent: an effectful function is Effect.fn("name"), and a named effect value has a span.
// ❌ noconst outcomeFor = (name: string) => Effect.gen(function* () { … }).pipe(Effect.catch(toFailed))const refresh = Effect.gen(function* () { … })
// ✅ yes: extra combinators as arguments, not .pipeconst outcomeFor = Effect.fn("outcomeFor")(function* (name: string) { … }, Effect.catch(toFailed))const refresh = Effect.gen(function* () { … }).pipe(Effect.withSpan("refresh"))Trigger:
- a declared function, a variable or an object member that only returns
Effect.gen(…), even with.pipe; - a variable or an object member whose value is
Effect.gen(…)withoutEffect.withSpanin its.pipe; Effect.fn(body)without a name;.pipeonEffect.fn(…)(…).
Exemptions and blind spots: Effect.gen inline as an argument (Layer.effect(Tag, Effect.gen(…)), Stream.unwrap, callbacks, it.effect). Recursive functions: see untraced-recursion.
Why: named stack traces and spans without wrapper functions. Combinators passed as arguments run inside the function’s span and receive its arguments: (effect, name) => effect.pipe(…).
Options: none
Fix: none