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

State effect - mutable state threaded through computation.

Supports both simple single-state usage and multiple independent states via tags.

## Tag Convention

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

## Simple Usage (default tag)

    use Skuld.Syntax
    alias Skuld.Effects.State

    comp do
      n <- State.get()
      _ <- State.put(n + 1)
      n
    end
    |> State.with_handler(0)
    |> Comp.run!()
    #=> 0

## Multiple States (explicit tags)

    comp do
      _ <- State.put(:counter, 0)
      _ <- State.modify(:counter, &(&1 + 1))
      count <- State.get(:counter)
      _ <- State.put(:name, "alice")
      name <- State.get(:name)
      {count, name}
    end
    |> State.with_handler(0, tag: :counter)
    |> State.with_handler("", tag: :name)
    |> Comp.run!()
    #=> {1, "alice"}

## Per-tag dispatch

Each tag gets its own handler sig (a module atom) and compact operation
atoms. The tag is encoded in the sig, not in the operation args, so
operations carry only the data they need:

- `get` → `Comp.effect(sig, State.Get)` — bare atom, zero allocation
- `put` → `Comp.effect(sig, {State.Put, value})` — minimal 2-tuple

# `__handle__`

Install State handler via catch clause syntax.

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

    catch
      State -> 0                          # initial value
      State -> {0, output: fn r, s -> {r, s} end}  # with opts

# `get_op`

```elixir
@spec get_op() :: atom()
```

Returns the get operation atom.

# `get_state`

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

Extract the state for the given tag from an env

# `gets`

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

Get a value derived from the state.

## Examples

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

# `gets`

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

# `modify`

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

Modify the state with a function, returning the old value.

## Examples

    State.modify(&(&1 + 1))              # use default tag
    State.modify(:counter, &(&1 + 1))    # use explicit tag

# `modify`

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

# `put_op`

```elixir
@spec put_op() :: atom()
```

Returns the put operation atom.

# `state_key`

# `with_handler`

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

Install a scoped State handler for a computation.

## Options

- `tag` - the state tag (default: `Skuld.Effects.State`)
- `output` - optional function `(result, final_state) -> new_result`
  to transform the result before returning.

## Examples

    # Simple usage with default tag
    comp do
      x <- State.get()
      _ <- State.put(x + 1)
      x
    end
    |> State.with_handler(0)
    |> Comp.run!()
    #=> 0

    # With explicit tag
    comp do
      x <- State.get(:counter)
      _ <- State.put(:counter, x + 1)
      x
    end
    |> State.with_handler(0, tag: :counter)
    |> Comp.run!()
    #=> 0

    # Include final state in result
    comp do
      _ <- State.modify(&(&1 + 1))
      :done
    end
    |> State.with_handler(5, output: fn result, state -> {result, state} end)
    |> Comp.run!()
    #=> {:done, 6}

    # Multiple states
    comp do
      a <- State.get(:a)
      b <- State.get(:b)
      {a, b}
    end
    |> State.with_handler(1, tag: :a)
    |> State.with_handler(2, tag: :b)
    |> Comp.run!()
    #=> {1, 2}

---

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