# `Skuld.Effects.Reader`
[🔗](https://github.com/mccraigmccraig/skuld/blob/main/lib/skuld/effects/reader.ex#L1)

Reader effect - access an immutable environment value.

Supports both simple single-context usage and multiple independent contexts via tags.

## Tag Convention

Tags should be module atoms — either the effect module itself (`Reader`)
for singleton context, or a derived atom (`MyApp.Config`, `Reader.DB`)
for namespaced contexts. Module atoms are globally unique by construction,
eliminating accidental collisions between independent pieces of context.

## Simple Usage (default tag)

    use Skuld.Syntax
    alias Skuld.Effects.Reader

    comp do
      cfg <- Reader.ask()
      cfg.name
    end
    |> Reader.with_handler(%{name: "alice"})
    |> Comp.run!()
    #=> "alice"

## Multiple Contexts (explicit tags)

    comp do
      db <- Reader.ask(:db)
      api <- Reader.ask(:api)
      {db.host, api.url}
    end
    |> Reader.with_handler(%{host: "localhost"}, tag: :db)
    |> Reader.with_handler(%{url: "https://api.example.com"}, tag: :api)
    |> Comp.run!()
    #=> {"localhost", "https://api.example.com"}

## Per-tag dispatch

Each tag gets its own handler sig (a module atom) and compact operation
atom. The tag is encoded in the sig, not in the operation args:

- `ask` → `Comp.effect(sig(tag), Reader.Ask)` — bare atom, zero allocation

# `__handle__`

Install Reader handler via catch clause syntax.

Accepts either `value` or `{value, opts}`:

    catch
      Reader -> %{config: true}
      Reader -> {%{config: true}, tag: :my_reader}

# `asks`

```elixir
@spec asks((term() -&gt; term())) :: Skuld.Comp.Types.computation()
```

Read and apply a function to the environment value.

## Examples

    Reader.asks(&Map.get(&1, :name))           # use default tag
    Reader.asks(:user, &Map.get(&1, :name))    # use explicit tag

# `asks`

```elixir
@spec asks(atom(), (term() -&gt; term())) :: Skuld.Comp.Types.computation()
```

# `get_context`

```elixir
@spec get_context(Skuld.Comp.Types.env(), atom()) :: term()
```

Extract the context value for the given tag from an env

# `local`

```elixir
@spec local((term() -&gt; term()), Skuld.Comp.Types.computation()) ::
  Skuld.Comp.Types.computation()
```

Run a computation with a modified environment value.

## Examples

    Reader.local(&Map.put(&1, :debug, true), comp)           # use default tag
    Reader.local(:config, &Map.put(&1, :debug, true), comp)  # use explicit tag

# `local`

```elixir
@spec local(atom(), (term() -&gt; term()), Skuld.Comp.Types.computation()) ::
  Skuld.Comp.Types.computation()
```

# `state_key`

# `with_handler`

```elixir
@spec with_handler(Skuld.Comp.Types.computation(), term(), keyword()) ::
  Skuld.Comp.Types.computation()
```

Install a scoped Reader handler for a computation.

## Options

- `tag` - the context tag (default: `Skuld.Effects.Reader`)

## Examples

    # Simple usage with default tag
    comp do
      cfg <- Reader.ask()
      cfg.name
    end
    |> Reader.with_handler(%{name: "alice"})
    |> Comp.run!()
    #=> "alice"

    # With explicit tag
    comp do
      db <- Reader.ask(:db)
      db.host
    end
    |> Reader.with_handler(%{host: "localhost"}, tag: :db)
    |> Comp.run!()
    #=> "localhost"

    # Multiple contexts
    comp do
      db <- Reader.ask(:db)
      cache <- Reader.ask(:cache)
      {db, cache}
    end
    |> Reader.with_handler(%{host: "db.local"}, tag: :db)
    |> Reader.with_handler(%{host: "cache.local"}, tag: :cache)
    |> Comp.run!()
    #=> {%{host: "db.local"}, %{host: "cache.local"}}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
