Skuld.Effects.Writer (skuld v0.1.26)
View SourceWriter effect - accumulate a log during computation.
Supports both simple single-log usage and multiple independent logs via tags.
Simple Usage (default tag)
use Skuld.Syntax
alias Skuld.Effects.Writer
comp do
_ <- Writer.tell("step 1")
_ <- Writer.tell("step 2")
:done
end
|> Writer.with_handler([], output: fn result, log -> {result, log} end)
|> Comp.run!()
#=> {:done, ["step 2", "step 1"]}Multiple Logs (explicit tags)
comp do
_ <- Writer.tell(:audit, "user logged in")
_ <- Writer.tell(:metrics, {:counter, :login, 1})
_ <- Writer.tell(:audit, "user viewed dashboard")
:ok
end
|> Writer.with_handler([], tag: :audit, output: fn r, log -> {r, log} end)
|> Writer.with_handler([], tag: :metrics)
|> Comp.run!()
#=> {:ok, ["user viewed dashboard", "user logged in"]}Scoped Operations and Throw
The scoped operations (listen, pass, censor) use a peek-before/peek-after
pattern to calculate captured logs. This means on abnormal exit (throw):
- Logs written before the throw persist in state (not rolled back)
listendoes not capture partial logs - the throw propagates outcensordoes not apply its transform - logs leak out untransformed
This "fire-and-forget" semantic is intentional for logging (you typically want to see logs leading up to an error).
Summary
Functions
Install Writer handler via catch clause syntax.
Censor: transform all logs written during a computation.
Clear the log.
Get the accumulated log from the environment.
Run a computation and capture its log output.
Run a computation that returns {value, log_transform_fn}.
Read the current log (reverse chronological order).
Returns the env.state key used for a given tag.
Append a message to the log.
Tell multiple messages.
Install a scoped Writer handler for a computation.
Functions
Install Writer handler via catch clause syntax.
Accepts nil, initial, or {initial, opts}:
catch
Writer -> nil # empty initial
Writer -> [] # explicit empty initial
Writer -> {[], output: fn r, l -> {r, Enum.reverse(l)} end}
@spec censor(Skuld.Comp.Types.computation(), (list() -> list())) :: Skuld.Comp.Types.computation()
Censor: transform all logs written during a computation.
Examples
Writer.censor(comp, &Enum.map(&1, fn m -> "[REDACTED]" end))
Writer.censor(:audit, comp, &Enum.reverse/1)
@spec censor(atom(), Skuld.Comp.Types.computation(), (list() -> list())) :: Skuld.Comp.Types.computation()
@spec clear(atom()) :: Skuld.Comp.Types.computation()
Clear the log.
Examples
Writer.clear() # use default tag
Writer.clear(:audit) # use explicit tag
@spec get_log(Skuld.Comp.Types.env(), atom()) :: [term()]
Get the accumulated log from the environment.
Examples
Writer.get_log(env) # use default tag
Writer.get_log(env, :audit) # use explicit tag
@spec listen(Skuld.Comp.Types.computation()) :: Skuld.Comp.Types.computation()
Run a computation and capture its log output.
Returns {result, captured_log} where captured_log contains only
the messages written during the inner computation.
Examples
Writer.listen(comp) # use default tag
Writer.listen(:audit, comp) # use explicit tag
@spec listen(atom(), Skuld.Comp.Types.computation()) :: Skuld.Comp.Types.computation()
@spec pass(Skuld.Comp.Types.computation()) :: Skuld.Comp.Types.computation()
Run a computation that returns {value, log_transform_fn}.
The transform function is applied to the logs written during the computation.
Examples
Writer.pass(comp) # use default tag
Writer.pass(:audit, comp) # use explicit tag
@spec pass(atom(), Skuld.Comp.Types.computation()) :: Skuld.Comp.Types.computation()
@spec peek(atom()) :: Skuld.Comp.Types.computation()
Read the current log (reverse chronological order).
Examples
Writer.peek() # use default tag
Writer.peek(:audit) # use explicit tag
Returns the env.state key used for a given tag.
Useful for configuring EffectLogger's state_keys filter.
Examples
# Only capture Writer log in EffectLogger snapshots
EffectLogger.with_logging(state_keys: [Writer.state_key(:audit)])
# Multiple logs
EffectLogger.with_logging(state_keys: [
Writer.state_key(:audit),
Writer.state_key(:metrics)
])
@spec tell(term()) :: Skuld.Comp.Types.computation()
Append a message to the log.
Examples
Writer.tell("message") # use default tag
Writer.tell(:audit, "message") # use explicit tag
@spec tell(atom(), term()) :: Skuld.Comp.Types.computation()
@spec tell_many([term()]) :: Skuld.Comp.Types.computation()
Tell multiple messages.
Examples
Writer.tell_many(["a", "b", "c"]) # use default tag
Writer.tell_many(:audit, ["a", "b", "c"]) # use explicit tag
@spec tell_many(atom(), [term()]) :: Skuld.Comp.Types.computation()
@spec with_handler(Skuld.Comp.Types.computation(), list(), keyword()) :: Skuld.Comp.Types.computation()
Install a scoped Writer handler for a computation.
Options
tag- the log tag (default:Skuld.Effects.Writer)output- optional function(result, final_log) -> new_resultto transform the result before returning.
Examples
# Simple usage with default tag
comp do
_ <- Writer.tell("step 1")
_ <- Writer.tell("step 2")
:done
end
|> Writer.with_handler()
|> Comp.run!()
#=> :done
# Include final log in result
comp do
_ <- Writer.tell("step 1")
:done
end
|> Writer.with_handler([], output: fn result, log -> {result, log} end)
|> Comp.run!()
#=> {:done, ["step 1"]}
# With explicit tag
comp do
_ <- Writer.tell(:audit, "action 1")
:done
end
|> Writer.with_handler([], tag: :audit, output: fn r, log -> {r, log} end)
|> Comp.run!()
#=> {:done, ["action 1"]}
# Multiple logs
comp do
_ <- Writer.tell(:foo, "message 1")
_ <- Writer.tell(:bar, "message 2")
:done
end
|> Writer.with_handler([], tag: :foo)
|> Writer.with_handler([], tag: :bar)
|> Comp.run!()
#=> :done