Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.
Docs menu / Using Generators

Using Generators

Effect offers a convenient syntax, similar to async/await, to write effectful code using generators.

Understanding Effect.gen

The Effect.gen utility simplifies the task of writing effectful code by utilizing JavaScript’s generator functions. This method helps your code appear and behave more like traditional synchronous code, which enhances both readability and error management.

Example (Performing Transactions with Discounts)

Let’s explore a practical program that performs a series of data transformations commonly found in application logic:

import { Effect } from "effect"
// Function to add a small service charge to a transaction amount
const addServiceCharge = (amount: number) => amount + 1
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number,
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from a
// database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Simulated asynchronous task to fetch a discount rate from a
// configuration file
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
// Assembling the program using a generator function
const program = Effect.gen(function* () {
// Retrieve the transaction amount
const transactionAmount = yield* fetchTransactionAmount
// Retrieve the discount rate
const discountRate = yield* fetchDiscountRate
// Calculate discounted amount
const discountedAmount = yield* applyDiscount(transactionAmount, discountRate)
// Apply service charge
const finalAmount = addServiceCharge(discountedAmount)
// Return the total amount after applying the charge
return `Final amount to charge: ${finalAmount}`
})
// Execute the program and log the result
Effect.runPromise(program).then(console.log)
// Output: Final amount to charge: 96

Key steps to follow when using Effect.gen:

  • Wrap your logic in Effect.gen
  • Use yield* to handle effects
  • Return the final result

If any of the effects that you handle inside of the generator with yield* fail, then the generator will stop and exit with that failure.

Comparing Effect.gen with async/await

If you are familiar with async/await, you may notice that the flow of writing code is similar.

Let’s compare the two approaches:

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number,
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(transactionAmount, discountRate)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (total: number, discountRate: number): Promise<number> =>
discountRate === 0
? Promise.reject(new Error("Discount rate cannot be zero"))
: Promise.resolve(total - (total * discountRate) / 100)
const fetchTransactionAmount = Promise.resolve(100)
const fetchDiscountRate = Promise.resolve(5)
export const program = async function () {
const transactionAmount = await fetchTransactionAmount
const discountRate = await fetchDiscountRate
const discountedAmount = await applyDiscount(transactionAmount, discountRate)
const finalAmount = addServiceCharge(discountedAmount)