Algae v1.2.3 Algae.Writer View Source

Algae.Writer helps capture the pattern of writing to a pure log or accumulated value, handling the bookkeeping for you.

If Algae.Reader is quasi-read-only, Algae.Writer is quasi-write-only. This is often used for loggers, but could be anything as long as the hidden value is a Witchcraft.Monoid.

There are many applications of Writers, but as an illustrative point, one could use it for logging across processes and time, since the log is carried around with the result in a pure fashion. The monadic DSL helps make using these feel more natural.

For an illustrated guide to Writers, see Thee Useful Monads.

Anatomy

  %Algae.Writer{writer: {value, log}}
                                
 # "explicit" value position    "hidden" position,
 #                               commonly used as a log

Examples

iex> use Witchcraft
...>
...> excite =
...>   fn string ->
...>     monad writer({0.0, "log"}) do
...>       tell string
...>
...>       excited <- return "#{string}!"
...>       tell " => #{excited} ... "
...>
...>       return excited
...>     end
...>   end
...>
...> {_, logs} =
...>   "Hi"
...>   |> excite.()
...>   >>> excite
...>   >>> excite
...>   |> censor(&String.trim_trailing(&1, " ... "))
...>   |> run()
...>
...> logs
"Hi => Hi! ... Hi! => Hi!! ... Hi!! => Hi!!!"

iex> use Witchcraft
...>
...> exponent =
...>   fn num ->
...>     monad writer({0, 0}) do
...>       tell 1
...>       return num * num
...>     end
...>   end
...>
...> initial = 42
...> {result, times} = run(exponent.(initial) >>> exponent >>> exponent)
...>
...> "#{initial}^#{round(:math.pow(2, times))} = #{result}"
"42^8 = 9682651996416"

Link to this section Summary

Functions

Run a writer, and run a function over the resulting log

Copy the log into the value position. This makes it accessible in do-notation

Similar to listen/1, but with the ability to adjust the copied log

Construct a Algae.Writer struct from a starting value and log

Run a function in the value portion of an Algae.Writer on the log

Extract the enclosed value and log from an Algae.Writer

Set the "log" portion of an Algae.Writer step

Similar to new/2, but taking a tuple rather than separate fields

Link to this section Types

Link to this section Functions

Link to this function

censor(writer, fun) View Source
censor(Algae.Writer.t(), (any() -> any())) :: Algae.Writer.t()

Run a writer, and run a function over the resulting log.

Examples

iex> 42
...> |> new(["hi", "THERE", "friend"])
...> |> censor(&Enum.reject(&1, fn log -> String.upcase(log) == log end))
...> |> run()
{42, ["hi", "friend"]}

iex> use Witchcraft
...>
...> 0
...> |> new(["logs"])
...> |> monad do
...>   tell ["Start"]
...>   tell ["BANG!"]
...>   tell ["shhhhhhh..."]
...>   tell ["LOUD NOISES!!!"]
...>   tell ["End"]
...>
...>   return 42
...> end
...> |> censor(&Enum.reject(&1, fn log -> String.upcase(log) == log end))
...> |> run()
{42, ["Start", "shhhhhhh...", "End"]}

Copy the log into the value position. This makes it accessible in do-notation.

Examples

iex> listen(%Algae.Writer{writer: {42, "hi"}})
%Algae.Writer{writer: {{42, "hi"}, "hi"}}

iex> use Witchcraft
...>
...> monad new(1, 1) do
...>   wr <- listen tell(42)
...>   tell 43
...>   return wr
...> end
%Algae.Writer{
  writer: {{%Witchcraft.Unit{}, 42}, 85}
}
Link to this function

listen(writer, fun) View Source
listen(Algae.Writer.t(), (log() -> log())) :: Algae.Writer.t()

Similar to listen/1, but with the ability to adjust the copied log.

Examples

iex> listen(%Algae.Writer{writer: {1, "hi"}}, &String.upcase/1)
%Algae.Writer{
  writer: {{1, "HI"}, "hi"}
}

Construct a Algae.Writer struct from a starting value and log.

Examples

iex> new()
%Algae.Writer{writer: {0, []}}

iex> new("hi")
%Algae.Writer{writer: {"hi", []}}

iex> new("ohai", 42)
%Algae.Writer{writer: {"ohai", 42}}

Run a function in the value portion of an Algae.Writer on the log.

Notice that the structure is similar to what somes out of listen/{1,2}

Algae.Writer{writer: {{_, function}, log}}

Examples

iex> pass(%Algae.Writer{writer: {{1, fn x -> x * 10 end}, 42}})
%Algae.Writer{writer: {1, 420}}

iex> use Witchcraft
...>
...> monad new("string", ["logs"]) do
...>   a <-  ["start"] |> tell() |> listen()
...>   tell ["middle"]
...>
...>   {value, logs} <- return a
...>   pass writer({{value, fn [log | _] -> [log | [log | logs]] end}, logs})
...>
...>   tell ["next is 42"]
...>   return 42
...> end
%Algae.Writer{
  writer: {42, ["start", "middle", "start", "start", "start", "next is 42"]}
}

Extract the enclosed value and log from an Algae.Writer.

Examples

iex> run(%Algae.Writer{writer: {"hi", "there"}})
{"hi", "there"}

iex> use Witchcraft
...>
...> half =
...>   fn num ->
...>     monad writer({0.0, ["log"]}) do
...>       let half = num / 2
...>       tell ["#{num} / 2 = #{half}"]
...>       return half
...>     end
...>   end
...>
...> run(half.(42) >>> half >>> half)
{
  5.25,
  [
    "42 / 2 = 21.0",
    "21.0 / 2 = 10.5",
    "10.5 / 2 = 5.25"
  ]
}

Set the "log" portion of an Algae.Writer step

Examples

iex> tell("secrets")
%Algae.Writer{writer: {%Witchcraft.Unit{}, "secrets"}}

iex> use Witchcraft
...>
...> monad %Algae.Writer{writer: {"string", 1}} do
...>   tell 42
...>   tell 43
...>   return "hey"
...> end
%Algae.Writer{writer: {"hey", 85}}

iex> use Witchcraft
...>
...> half =
...>   fn num ->
...>     monad writer({0.0, ["log"]}) do
...>       let half = num / 2
...>       tell ["#{num} / 2 = #{half}"]
...>       return half
...>     end
...>   end
...>
...> run(half.(42.0) >>> half >>> half)
{
  5.25,
  [
    "42.0 / 2 = 21.0",
    "21.0 / 2 = 10.5",
    "10.5 / 2 = 5.25"
  ]
}

Similar to new/2, but taking a tuple rather than separate fields.

Examples

iex> writer({"ohai", 42})
%Algae.Writer{writer: {"ohai", 42}}