Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce with #3902

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

Introduce with #3902

josevalim opened this issue Oct 23, 2015 · 2 comments

Comments

@josevalim
Copy link
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
Copy link
Member Author

@lexmag lexmag self-assigned this Oct 30, 2015
@lexmag lexmag mentioned this issue Nov 7, 2015
@liveforeverx
Copy link
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

3 participants