Algae v1.1.0 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 Writer
s, 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 Writer
s,
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
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}
}
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"}
}
new(any, Witchcraft.Monoid.t) :: Algae.Writer.t
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}}