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?
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} .
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.
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).
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With