View Source Funx.Monad.Writer (funx v0.1.3)

The Funx.Monad.Writer module defines the Writer monad, which threads a log alongside a computed result.

Logs are accumulated using a Monoid implementation, injected lazily at runtime. This makes the Writer monad flexible and monoid-polymorphic—supporting lists, strings, or any user-defined monoid.

Core functions

  • pure/1 – Wraps a result with an empty log.
  • writer/1 – Wraps a result and an explicit log.
  • tell/1 – Emits a log with no result.
  • listen/1 – Returns both result and log as a pair.
  • censor/2 – Applies a function to transform the final log.
  • pass/1 – Uses a log-transforming function returned from within the computation.
  • run/2 – Executes the Writer and returns a %Writer.Result{} with result and log.
  • eval/2 – Executes and returns only the result.
  • exec/2 – Executes and returns only the log.

By default, the ListConcat monoid is used unless a different monoid is passed to run, eval, or exec.

This module also implements the Funx.Monad protocol.

Summary

Types

Represents a computation that produces a result along with a log, accumulated using a monoid.

Functions

Transforms the final log by applying a function to it.

Executes the Writer and returns only the final result value.

Executes the Writer and returns only the final accumulated log.

Captures the current log and returns it alongside the result.

Applies a log-transforming function that is returned from within the computation.

Wraps a value with no log.

Executes the Writer and returns both the result and the final accumulated log.

Appends a log value using the monoid, returning :ok as the result.

Wraps both a value and a raw log into the Writer context.

Types

@type t(a) :: %Funx.Monad.Writer{writer: (term() -> {a, term()})}

Represents a computation that produces a result along with a log, accumulated using a monoid.

The internal writer function takes an initial monoid and returns a {value, monoid} tuple, where the monoid contains the accumulated log.

Functions

@spec censor(t(a), (term() -> term())) :: t(a) when a: term()

Transforms the final log by applying a function to it.

The result remains unchanged—only the log is modified.

Example

iex> writer = Funx.Monad.Writer.writer({"ok", [:a, :b]})
iex> censored = Funx.Monad.Writer.censor(writer, fn log -> Enum.reverse(log) end)
iex> result = Funx.Monad.Writer.run(censored)
iex> result.value
"ok"
iex> result.log
[:b, :a]
Link to this function

eval(writer, monoid \\ %ListConcat{})

View Source
@spec eval(t(a), monoid) :: a when a: term(), monoid: term()

Executes the Writer and returns only the final result value.

Uses ListConcat by default.

Example

iex> writer =
...>   Funx.Monad.Writer.writer({10, [:init]})
...>   |> Funx.Monad.bind(fn x ->
...>     Funx.Monad.Writer.tell([:logged])
...>     |> Funx.Monad.bind(fn _ -> Funx.Monad.Writer.pure(x * 2) end)
...>   end)
iex> Funx.Monad.Writer.eval(writer)
20
Link to this function

exec(writer, monoid \\ %ListConcat{})

View Source
@spec exec(t(a), monoid) :: log when a: term(), monoid: term(), log: term()

Executes the Writer and returns only the final accumulated log.

Uses ListConcat by default.

Example

iex> writer =
...>   Funx.Monad.Writer.writer({:ok, [:step1]})
...>   |> Funx.Monad.bind(fn _ -> Funx.Monad.Writer.tell([:step2]) end)
iex> Funx.Monad.Writer.exec(writer)
[:step1, :step2]
@spec listen(t(a)) :: t({a, log}) when a: term(), log: term()

Captures the current log and returns it alongside the result.

The log remains unchanged—only the result is modified to include it.

Example

iex> writer = Funx.Monad.Writer.writer({"done", [:start, :finish]})
iex> listened = Funx.Monad.Writer.listen(writer)
iex> result = Funx.Monad.Writer.run(listened)
iex> result.value
{"done", [:start, :finish]}
iex> result.log
[:start, :finish]
@spec pass(t({a, (log -> log)})) :: t(a) when a: term(), log: term()

Applies a log-transforming function that is returned from within the computation.

This allows the result of a computation to include not only a value, but also a function that modifies the final accumulated log.

The input to pass/1 must be a Writer containing a tuple {result, f}, where f is a function from log to log. This function will be applied to the final log just before it's returned.

Example

iex> result =
...>   Funx.Monad.Writer.pure({"done", fn log -> log ++ [:transformed] end})
...>   |> Funx.Monad.Writer.pass()
...>   |> Funx.Monad.Writer.run()
iex> result.value
"done"
iex> result.log
[:transformed]
@spec pure(a) :: t(a) when a: term()

Wraps a value with no log.

Example

iex> writer = Funx.Monad.Writer.pure(42)
iex> result = Funx.Monad.Writer.run(writer)
iex> result.value
42
iex> result.log
[]
Link to this function

run(writer, monoid \\ %ListConcat{})

View Source
@spec run(t(a), monoid) :: Funx.Monad.Writer.Result.t(a, log)
when a: term(), log: term(), monoid: term()

Executes the Writer and returns both the result and the final accumulated log.

By default, it uses ListConcat unless a monoid is explicitly passed.

Example

iex> writer = Funx.Monad.Writer.writer({"ok", [:a, :b]})
iex> result = Funx.Monad.Writer.run(writer)
iex> result.value
"ok"
iex> result.log
[:a, :b]
@spec tell(log) :: t(:ok) when log: term()

Appends a log value using the monoid, returning :ok as the result.

Example

iex> writer = Funx.Monad.Writer.tell([:event])
iex> result = Funx.Monad.Writer.run(writer)
iex> result.value
:ok
iex> result.log
[:event]
@spec writer({a, term()}) :: t(a) when a: term()

Wraps both a value and a raw log into the Writer context.

Example

iex> writer = Funx.Monad.Writer.writer({:ok, [:step1, :step2]})
iex> result = Funx.Monad.Writer.run(writer)
iex> result.value
:ok
iex> result.log
[:step1, :step2]