Creating Effects
Most real programs need to bring the outside world into Effect: a value you
already have, some synchronous code, a Promise-based client, a possibly-null
lookup, or a callback API. Effect provides a small, precise set of constructors
for each case. Picking the right one is mostly about one question: can this
fail, and if so, how should the failure be typed?
import { Effect, Schema } from "effect"
class InvalidPayload extends Schema.TaggedErrorClass<InvalidPayload>()( "InvalidPayload", { input: Schema.String, cause: Schema.Defect }) {}
class UserLookupError extends Schema.TaggedErrorClass<UserLookupError>()( "UserLookupError", { userId: Schema.Number, cause: Schema.Defect }) {}
class MissingWorkspaceId extends Schema.TaggedErrorClass<MissingWorkspaceId>()( "MissingWorkspaceId", {}) {}
const requestHeaders = new Map<string, string>([["x-request-id", "req_1"]])
const users = new Map([ [1, { id: 1, name: "Ada" }], [2, { id: 2, name: "Lin" }]])
// `Effect.succeed` wraps a value you already have in memory.export const fromValue = Effect.succeed({ env: "prod", retries: 3 })
// `Effect.sync` wraps a synchronous side effect that will NOT throw.export const fromSyncSideEffect = Effect.sync(() => Math.random())
// `Effect.try` wraps synchronous code that MAY throw, mapping the thrown// value into a typed error.export const parsePayload = Effect.fn("parsePayload")((input: string) => Effect.try({ try: () => JSON.parse(input) as { readonly userId: number }, catch: (cause) => new InvalidPayload({ input, cause }) }))
// `Effect.tryPromise` wraps a Promise-based API that may reject or throw.export const fetchUser = Effect.fn("fetchUser")((userId: number) => Effect.tryPromise({ async try() { const user = users.get(userId) if (!user) throw new Error(`Missing user ${userId}`) return user }, catch: (cause) => new UserLookupError({ userId, cause }) }))
// `Effect.fromNullishOr` turns a nullable value into a typed effect, then we// map the built-in NoSuchElementError to a domain error.export const fromNullishHeader = Effect.fromNullishOr( requestHeaders.get("x-workspace-id")).pipe(Effect.mapError(() => new MissingWorkspaceId()))
// `Effect.callback` wraps a callback-style asynchronous API.export const fromCallback = Effect.callback<number>((resume) => { const timeoutId = setTimeout(() => resume(Effect.succeed(200)), 10) // Return a finalizer so interruption can cancel the source. return Effect.sync(() => clearTimeout(timeoutId))})From values
Section titled “From values”Use Effect.succeed(value) to lift a value you already have, and
Effect.fail(error) for the failure channel. Both are eager about the value but
lazy about running - the effect still does nothing until executed.
import { Effect } from "effect"
const ok = Effect.succeed(42) // Effect<number>const bad = Effect.fail("nope" as const) // Effect<never, "nope">From synchronous code
Section titled “From synchronous code”Effect.sync(thunk)- for synchronous side effects that are guaranteed not to throw (reading a clock-free constant, generating a random number, pushing to an in-memory buffer). If the thunk does throw, the exception is treated as an unrecoverable defect, not a typed error.Effect.try({ try, catch })- for synchronous code that can throw (likeJSON.parse). Thecatchfunction maps the thrown value into a typed error in the error channel.
import { Effect } from "effect"
// Will not throw -> syncconst random = Effect.sync(() => Math.random())
// Might throw -> try, with the error mapped into the typeconst parse = (input: string) => Effect.try({ try: () => JSON.parse(input) as unknown, catch: (cause) => new Error(`Invalid JSON: ${String(cause)}`) })From promises
Section titled “From promises”Effect.promise(thunk)- when thePromiseis guaranteed not to reject. A rejection becomes a defect.Effect.tryPromise({ try, catch })- when rejection is expected. Thecatchfunction turns the rejection into a typed error.
The thunk receives an AbortSignal, so a well-behaved client can be cancelled
when the effect is interrupted:
import { Effect } from "effect"
const getJson = (url: string) => Effect.tryPromise({ // The signal is wired to Effect interruption. try: (signal) => fetch(url, { signal }).then((res) => res.json()), catch: (cause) => new Error(`Request failed: ${String(cause)}`) })Notice there is no async/await in your Effect code - the Promise lives
entirely inside the try thunk, and Effect takes it from there.
From nullable values
Section titled “From nullable values”Effect.fromNullishOr(value) succeeds with the value when it is non-null and
fails with a built-in NoSuchElementError when it is null or undefined.
Map that into a domain error when you want a meaningful failure:
import { Effect } from "effect"
const env = new Map<string, string>([["PORT", "8080"]])
const requireEnv = (key: string) => Effect.fromNullishOr(env.get(key)).pipe( Effect.mapError(() => new Error(`Missing env var: ${key}`)) )From callbacks
Section titled “From callbacks”Effect.callback adapts a callback-style asynchronous API. You receive a
resume function and call it with an effect (typically Effect.succeed or
Effect.fail) when the operation completes. Returning a finalizer effect lets
Effect cancel the underlying source if the fiber is interrupted before it
resolves.
import { Effect } from "effect"
const delay = (ms: number) => Effect.callback<void>((resume) => { const id = setTimeout(() => resume(Effect.succeed(undefined)), ms) // Cleanup on interruption. return Effect.sync(() => clearTimeout(id)) })Choosing a constructor
Section titled “Choosing a constructor”| You have… | Use | Failure channel |
|---|---|---|
| A value in memory | Effect.succeed | none |
| An error value | Effect.fail | the error you pass |
| Sync code that won’t throw | Effect.sync | defect if it throws |
| Sync code that may throw | Effect.try | mapped by catch |
A non-rejecting Promise | Effect.promise | defect if it rejects |
A Promise that may reject | Effect.tryPromise | mapped by catch |
A possibly-null value | Effect.fromNullishOr | NoSuchElementError |
| A callback-based async API | Effect.callback | whatever you resume with |
Once you have an effect, compose it with pipelines and handle the failures with Error Management.