Effect.gen Quickstart
Basic Generator
import { Effect, Console } from "effect"
const program = Effect.gen(function*() {
yield* Console.log("Starting...")
const a = yield* Effect.succeed(10)
const b = yield* Effect.succeed(20)
return a + b
})
Effect.runPromise(program).then(console.log)
// Output:
// Starting...
// 30
Each yield* runs an effect and binds its success value. If any effect fails, execution stops there. This reads like async/await but with synchronous-looking control flow and full type inference.
Error Handling with return yield*
import { Effect, Schema, Console } from "effect"
class ValidationError extends Schema.TaggedError<ValidationError>()("ValidationError", {
message: Schema.String
}) {}
const validate = (age: number) =>
Effect.gen(function*() {
if (age < 0) {
return yield* new ValidationError({ message: "Age cannot be negative" })
}
if (age > 150) {
return yield* new ValidationError({ message: "Age seems unrealistic" })
}
return age
})
const program = Effect.gen(function*() {
const result = yield* validate(200).pipe(
Effect.catchTag("ValidationError", (err) =>
Effect.gen(function*() {
yield* Console.log(`Caught: ${err.message}`)
return 0
})
)
)
return result
})
Effect.runPromise(program).then(console.log)
// Output:
// Caught: Age seems unrealistic
// 0
Gotcha: Always use
return yield*when yielding a failing effect. Withoutreturn, TypeScript won’t narrow the type, and the generator continues with unreachable code after the error.
Logging
import { Effect, Console } from "effect"
const program = Effect.gen(function*() {
yield* Effect.logInfo("Fetching data...")
yield* Effect.logDebug("Debug details here")
const data = yield* Effect.succeed({ id: 1, name: "Alice" })
yield* Console.log(`Got user: ${data.name}`)
return data
})
Effect.runPromise(program)
Effect has structured logging with levels: logInfo, logDebug, logWarning, logError, and log. Logs are not printed to stdout by default in v4. Use Console.log for direct stdout output, or configure a logger layer.
Effect.fn for Named Functions
import { Effect, Schema } from "effect"
class FetchError extends Schema.TaggedError<FetchError>()("FetchError", {
url: Schema.String
}) {}
export const fetchJson = Effect.fn("fetchJson")(
function*(url: string) {
yield* Effect.logInfo(`Fetching ${url}`)
return yield* Effect.tryPromise({
try: () => fetch(url).then((r) => r.json()),
catch: () => new FetchError({ url })
})
},
Effect.catchTag("FetchError", (err) =>
Effect.gen(function*() {
yield* Effect.logWarning(`Retrying failed: ${err.url}`)
return null
})
)
)
// Usage
const program = Effect.gen(function*() {
const data = yield* fetchJson("https://api.example.com/users")
return data
})
Effect.runPromise(program)
Effect.fn wraps a generator function so it returns an Effect. Pass a name string to get better stack traces and automatic tracing spans. Additional arguments after the function are combinators applied to every call, so you don’t need .pipe.
Tip: Don’t use
.pipeonEffect.fnresults. Pass combinators as extra arguments toEffect.fninstead.
A Practical Example
import { Effect, Schema, Console } from "effect"
class HttpError extends Schema.TaggedError<HttpError>()("HttpError", {
status: Schema.Number,
url: Schema.String
}) {}
class ParseError extends Schema.TaggedError<ParseError>()("ParseError", {
message: Schema.String
}) {}
const fetchUser = Effect.fn("fetchUser")(
function*(id: number) {
yield* Effect.logInfo(`Fetching user ${id}`)
const response = yield* Effect.tryPromise({
try: () => fetch(`https://jsonplaceholder.typicode.com/users/${id}`),
catch: () => new HttpError({ status: 0, url: `users/${id}` })
})
if (!response.ok) {
return yield* new HttpError({ status: response.status, url: `users/${id}` })
}
const json = yield* Effect.tryPromise({
try: () => response.json(),
catch: () => new ParseError({ message: `Failed to parse response for user ${id}` })
})
yield* Effect.logInfo(`Successfully fetched user ${id}`)
return json
}
)
const program = Effect.gen(function*() {
const user = yield* fetchUser(1).pipe(
Effect.catchTag("HttpError", (err) =>
Effect.gen(function*() {
yield* Console.log(`HTTP ${err.status} on ${err.url}`)
return null
})
),
Effect.catchTag("ParseError", (err) =>
Effect.gen(function*() {
yield* Console.log(`Parse error: ${err.message}`)
return null
})
)
)
yield* Console.log(`Result: ${JSON.stringify(user)}`)
return user
})
Effect.runPromise(program)
Next Steps
- Handle Errors - Full error handling patterns
- Define Services - Dependency injection with Context.Service
- Validate Data with Schema - Schema-driven validation