Introduce with #3902

Closed
josevalim opened this Issue Oct 23, 2015 · 2 comments

Projects

None yet

3 participants

@josevalim
Member

with is for's younger brother. Imagine we have two functions:

def ok(x), do: {:ok, x}
def error(x), do: {:error, x}

While for is used to match on values out of a collection:

for {:ok, x} <- [ok(1), error(2), ok(3)], do: x
#=> [1, 3]

with is used to match on values directly:

with {:ok, x} <- ok(1),
     {:ok, y} <- ok(2),
     do: {:ok, x + y}
#=> {:ok, 3}

Because all values matched, the do block was executed, returning its result. If a value does not match, it will abort the chain:

with {:ok, x} <- ok(1),
     {:ok, y} <- error(2),
     do: {:ok, x + y}
#=> {:error, 2}

Since error(2) did not match {:ok, y}, the with chain aborted, returning {:error, 2}.

There are many different scenarios on every day Elixir code that we can use with. For example, it is useful to avoid nesting "case"s:

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

Can now be rewritten as:

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

Another example is Plug itself. Plug will only call the next plug if the :halted field in the connection is false. Therefore a whole plug pipeline can be written as:

with %{halted: false} = conn <- plug1(conn, opts1),
     %{halted: false} = conn <- plug2(conn, opts2),
     %{halted: false} = conn <- plug3(conn, opts3),
     do: conn

If any of them does not match, because :halted is true, the pipeline is aborted.

Similarly to for, variables bound inside with won't leak. Also similar to for, with allows "bare expressions". For example, imagine you need to calculate a value before calling the next match, you may write:

with {:ok, binary} <- File.read(path),
     header = parse_header(binary),
     {:ok, data} <- :beam_lib.chunks(header, :abstract_code),
     do: {:ok, wrap(data)}
@josevalim josevalim added this to the v1.2.0 milestone Oct 23, 2015
@lexmag lexmag self-assigned this Oct 30, 2015
@lexmag lexmag referenced this issue Nov 7, 2015
Merged

Introduce with #3943

@liveforeverx
Contributor

Error monad build-in in elixir!!! It is great! ๐Ÿ‘ ๐Ÿ‘ ๐Ÿ‘ โค๏ธ ๐Ÿ’š ๐Ÿ’› ๐Ÿ’™

This is only one thing, why some projects, that I've seen have taken monad library or rebuild error monad themself.

@josevalim josevalim closed this in #3943 Nov 13, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment