Skuld.Effects.State (skuld v0.23.0)

View Source

State effect - mutable state threaded through computation.

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

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:

  • getComp.effect(sig, State.Get) — bare atom, zero allocation
  • putComp.effect(sig, {State.Put, value}) — minimal 2-tuple

Summary

Functions

Install State handler via catch clause syntax.

Returns the get operation atom.

Extract the state for the given tag from an env

Get a value derived from the state.

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

Returns the put operation atom.

Returns the env.state key used for a given tag.

Install a scoped State handler for a computation.

Functions

__handle__(comp, initial)

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()

@spec get_op() :: atom()

Returns the get operation atom.

get_state(env, tag \\ Skuld.Effects.State)

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

Extract the state for the given tag from an env

gets(f)

@spec gets((term() -> 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(tag, f)

@spec gets(atom(), (term() -> term())) :: Skuld.Comp.Types.computation()

modify(f)

@spec modify((term() -> 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(tag, f)

@spec modify(atom(), (term() -> term())) :: Skuld.Comp.Types.computation()

put_op()

@spec put_op() :: atom()

Returns the put operation atom.

state_key(tag)

@spec state_key(atom()) :: atom()

Returns the env.state key used for a given tag.

Useful for configuring EffectLogger's state_keys filter.

Examples

# Only capture State effect data in EffectLogger snapshots
EffectLogger.with_logging(state_keys: [State.state_key(MyApp.Counter)])

# Multiple states
EffectLogger.with_logging(state_keys: [
  State.state_key(:counter),
  State.state_key(:user)
])

with_handler(comp, initial, opts \\ [])

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}