Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use the "with" keyword in Elixir and what is it for?

Tags:

elixir

In elixir 1.2 they've included the keyword "with", but it's not completely clear to me what it is for.

How and in which situation would I use it?

like image 471
diogovk Avatar asked Dec 10 '15 19:12

diogovk


People also ask

What is with statement Elixir?

with statements do not need to handle non-matching clauses. Instead, the with statement will return the non-matching clause directly to the parent scope. Where the function Users. create_user/1 can return either {:ok, user} or {:error, changeset} .

Does Elixir have statements?

One of the most fundamental control structures of Elixir is with . with statements are similar to case statements in that they execute tasks if the conditions are fulfilled.


2 Answers

In versions of Elixir prior to 1.2 when using functions in a pipeline, you would either have to use a monad library or nest case statements (which could be refactored using private functions, but still would end up being verbose). with/1 allows a different way to solve this problem.

Here is an example from the original proposal:

case File.read(path) do   {:ok, binary} ->     case :beam_lib.chunks(binary, :abstract_code) do       {:ok, data} ->         {:ok, wrap(data)}       error ->         error     end   error ->     error end 

Here is the same thing refactored to use functions:

path |> File.read() |> read_chunks() |> wrap()  defp read_chunks({:ok, binary}) do   {:ok, :beam_lib.chunks(binary, :abstract_code)} end defp read_chunks(error), do: error  defp wrap({:ok, data}) do   {:ok, wrap(data)} end defp wrap(error), do: error 

And the same code using with:

with {:ok, binary} <- File.read(path),      {:ok, data} <- :beam_lib.chunks(binary, :abstract_code),      do: {:ok, wrap(data)} 

This works because with will only keep chaining if the value matches the pattern on the left. If not then the chain is aborted and the first non-matching result is returned. For example if the file does not exist then File.read(path) will return {:error, :enoent} - this does not match {:ok, binary} so the with/1 call will return {:error, :enoent}.

It is worth noting that with can be used with any pattern, not just {:ok, foo} and {:error, reason} (although it is a very common use case).

like image 138
Gazler Avatar answered Sep 27 '22 20:09

Gazler


You can also chain "bare expressions", as the doc says:

with {:ok, binary} <- File.read(path),      header = parse_header(binary),      {:ok, data} <- :beam_lib.chunks(header, :abstract_code),      do: {:ok, wrap(data)} 

The variable header will be available only inside the with statement. More info at https://gist.github.com/josevalim/8130b19eb62706e1ab37

like image 45
leandrocp Avatar answered Sep 27 '22 20:09

leandrocp