# 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?**

```ts
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

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.

```ts
import { Effect } from "effect"

const ok = Effect.succeed(42) //      Effect<number>
const bad = Effect.fail("nope" as const) // Effect<never, "nope">
```

## 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
  (like `JSON.parse`). The `catch` function maps the thrown value into a typed
  error in the error channel.

```ts
import { Effect } from "effect"

// Will not throw -> sync
const random = Effect.sync(() => Math.random())

// Might throw -> try, with the error mapped into the type
const parse = (input: string) =>
  Effect.try({
    try: () => JSON.parse(input) as unknown,
    catch: (cause) => new Error(`Invalid JSON: ${String(cause)}`)
  })
```

## From promises

- **`Effect.promise(thunk)`** - when the `Promise` is guaranteed not to reject.
  A rejection becomes a defect.
- **`Effect.tryPromise({ try, catch })`** - when rejection is expected. The
  `catch` function 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:

```ts
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

`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:

```ts
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

`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.

```ts
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

| 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](https://effect.plants.sh/essentials/building-pipelines/)
and handle the failures with [Error Management](https://effect.plants.sh/error-management/).