In part 22 of my Elixir journey I wanted to share a quick example I put together that helped me wrap my head around `with` in the language.
With
When I first read the Elixir getting started documentation for `with` I learned that it's a common solution to the nested `case` problem I frequently run into. To illustrate this I wrote a program that adds together all the numbers in a list. The catch is we only want to `sum` if that list has no duplicate numbers.
defmodule MyProgram do
def calc(list) do
case duplicate(list) do
{:ok} ->
case sum(list) do
{:ok, total} ->
IO.puts "total #{total}"
{:error} ->
IO.puts "error"
end
{:error} ->
IO.puts "duplicate found!"
end
end
end
If you run the program with any duplicates you will see `IO.puts` complaining about a duplicate. If you run the program with unique numbers you will instead see the total.
MyProgram.calc([1, 1, 2])
The power of `with` is that instead of nesting you can chain together a series of match clauses and Elixir will apply something like a circuit break for you. In this scenario, if the `duplicate` function fails to match, Elixir will simply omit execution of the `sum` function altogether.
defmodule MyProgram do
def calc(list) do
with {:ok} <- duplicate(list),
{:ok, total} <- sum(list) do
IO.puts "total #{total}"
else
{:error} ->
IO.puts "duplicate found!"
end
end
end
The entire module including both `sum` and `duplicate` functions for anyone interested.
defmodule MyProgram do
def sum([head | tail]) do
{:ok, Enum.reduce(tail, head, &(&1 + &2))}
end
def duplicate([]), do: {:ok}
def duplicate([head | tail]) do
case Enum.any?(tail, &(&1 == head)) do
true ->
{:error}
false ->
duplicate(tail)
end
end
def calc(list) do
with {:ok} <- duplicate(list),
{:ok, total} <- sum(list) do
IO.puts "total #{total}"
else
{:error} ->
IO.puts "duplicate found!"
end
end
end