Either Monad in Elixir
In the beginning was the Hype, and the Hype was with Monads, and the Hype was Monad.
Either this hype makes sense or not. Try it yourself. Right? Left unclear? Anyway.
Yes, Anyway
monad were my favorite one, if it were existing.
Monads are known to be good in handling side effects and dealing with side effects. Monads are very simple (despite their cryptographogastroenterologic name.) Monads are after all handy, resulting in an easy-to-read code, which is nowadays considered to be even more valuable then producing-expected-result and even correct code.
OK, jokes aside.
There are many 3rd party libraries, implementing the basic monadic behavior, but the truth is Elixir comes with monads onboard. Out of the box. I am not kidding you.
Our today’s special guest is
Kernel.SpecialForms.with/1
.
It is widely used to chain the simple atomic functions returning {:ok, result}
tuples:
with {:ok, [file | _tail]} <- File.ls,
{:ok, content} <- File.read(file),
do: IO.puts content
And you know what? It behaves as simple Either
monad. That said,
if the match on any subsequent step failed, the execution will immediately stop
and RHO will be returned.
Let’s check this trivial contrived example:
iex|1 ▶ with 1 <- 1,
...|1 ▶ 2 <- "HERE WE GO",
...|1 ▶ 3 <- 3, do: "HERE WE WON’T GO"
"HERE WE GO"
The second match failed and not matched term had beed returned. It’s amazing, is not it?
Let’s turn back to the first example with files. What would be returned in case
we failed to list files? Yes: {:error, :enoent}
(or similar, and we would
safely skip all the subsequent steps, directly returning {:error, :enoent}
to the caller!) What is this?—this is an Either
monad.
That simple.
Bonus Track
It’s not mentioned anywhere in the documentation, or at least I could not find it,
but with
special form apparently supports else
with matching clauses.
Consider the following code:
with {:ok, [file | _tail]} <- File.ls("NOWAY"),
{:ok, content} <- File.read(file) do
IO.puts content
else
{:error, :enoent} -> IO.puts "NO SUCH FILE"
unexpected -> IO.puts "#{inspect unexpected} happened"
end
Yes, it would spit "NO SUCH FILE"
message out to the console. Awesome!