Algae.Writer (Algae v1.3.1) 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
Specs
log() :: Witchcraft.Monoid.t()
Specs
t() :: %Algae.Writer{writer: writer()}
Specs
value() :: any()
Specs
Link to this section Functions
Specs
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"]}
Specs
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}
}
Specs
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"}
}
Specs
new(any(), Witchcraft.Monoid.t()) :: 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}}
Specs
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"]}
}
Specs
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"
]
}
Specs
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"
]
}
Specs
Similar to new/2
, but taking a tuple rather than separate fields.
Examples
iex> writer({"ohai", 42})
%Algae.Writer{writer: {"ohai", 42}}