Is the with do: else: available in 1.3.0?

I have some use cases where with combined with an else block would really help
but it doesn’t compile.
I read here railway-development-in-elixir-using-with
that the else block becomes available in 1.3.0


Yes, it is. Can you post a code sample and the error message you are getting?


From the changes in 1.3.0 on github:

[Kernel] Support else chunks in with

It’s easy to sometimes get the syntax not quite right with them, and it can be difficult to troubleshoot. I use with all of the time now and I made a gist on many of the syntax variations, and I’ve finally come to the following syntax variation that I like:

    # You can comment each line with the paren syntax
    {:ok, bar} <- foo(),
    {:ok, result} <- baz(bar)
) do
    {:ok, result}
    # To show that you can match on the error returned
    {:error, reason} -> {:error, reason}
    error -> {:error, inspect error}

There are a couple of points to remember:

  1. The with block proper requires the arrows to point to the left. (This still trips me up sometimes).
  2. There is no , after the last with block clause. (This also gets me if I’m copy/pasting and won’t compile)
  3. In the else block, we’re matching on the error, so the arrows go to the right.

I prefer this paren-style version because it makes things clearer to me on where I need the commas, where I need left arrows, and it separates visually the happy path return do statement more clearly to me.


The code that doesn’t compile, looks like this:

with {_, responder, _, _} <- get_child(state.supervisor, :responder),
     {_, interface, _, _} <- get_child(state.supervisor, :interface),
     do: Responder.Supervisor.respond(responder, {reaction, interface}),
   else: IO.puts "not ok"

and the compilation error:

== Compilation error on file lib/dobar/robot.ex ==
** (FunctionClauseError) no function clause matching in :elixir_with.expand_else/2

(elixir) src/elixir_with.erl:58: :elixir_with.expand_else({{:., [line: 53], [{:aliases, [counter: 0, line: 53], [:IO]}, :puts]}, [line: 53], [“not ok”]}, %Macro.Env{aliases: [{Interface, Dobar.Interface}, {Conversation, Dobar.Conversation}, {Responder, Dobar.Responder}, {Intent, Dobar.Intent}, {Reaction, Dobar.Reaction}], context: nil, context_modules: [Dobar.Robot], export_vars: nil, file: “/Users/dragos/Playground/Elixir/dobar/lib/dobar/robot.ex”, function: {:handle_info, 2}, functions: [{Kernel, [!=: 2, !==: 2, *: 2, +: 1, +: 2, ++: 2, -: 1, -: 2, --: 2, /: 2, <: 2, <=: 2, ==: 2, ===: 2, =~: 2, >: 2, >=: 2, abs: 1, apply: 2, apply: 3, binary_part: 3, bit_size: 1, byte_size: 1, div: 2, elem: 2, exit: 1, function_exported?: 3, get_and_update_in: 3, get_in: 2, hd: 1, inspect: 1, inspect: 2, is_atom: 1, is_binary: 1, is_bitstring: 1, is_boolean: 1, is_float: 1, is_function: 1, is_function: 2, is_integer: 1, is_list: 1, is_map: 1, …]}], lexical_tracker: #PID<0.144.0>, line: 45, macro_aliases: [], macros: [{Kernel, [!: 1, &&: 2, …: 2, <>: 2, @: 1, alias!: 1, and: 2, binding: 0, binding: 1, def: 1, def: 2, defdelegate: 2, defexception: 1, defimpl: 2, defimpl: 3, defmacro: 1, defmacro: 2, defmacrop: 1, defmacrop: 2, defmodule: 2, defoverridable: 1, defp: 1, defp: 2, defprotocol: 2, defstruct: 1, destructure: 2, get_and_update_in: 2, if: 2, in: 2, is_nil: 1, match?: 2, or: 2, pop_in: 1, put_in: 2, raise: 1, raise: 2, reraise: 2, reraise: 3, …]}], module: Dobar.Robot, requires: [GenServer, Kernel, Kernel.Typespec], vars: [reaction: nil, state: nil]})
(elixir) src/elixir_with.erl:48: :elixir_with.expand/3
lib/dobar/robot.ex:45: (module)

thanks for the reply - yes, i know about the <- when matching inside with and everything works
until i try to add an else block.
Now that i saw the syntax you’re using, i think i’m gonna give it a try.


That isn’t how else works. else works like else in try where you have patterns on the LHS IE. I’m not entirely sure if it’s possible to do else: with that syntax actually.

What version of elixir are you on specifically?

This code here produces a much nicer error for me:

    with :foo <- :foo,
      :bar <- :bar,
      do: :baz,
    else: IO.puts "yo"
** (CompileError) mix.exs:7: expected -> clauses for else in with
    (elixir) src/elixir_with.erl:48: :elixir_with.expand/3
    mix.exs:6: (module)

$ elixir --version
Erlang/OTP 19 [erts-8.0.2] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Elixir 1.3.0

Yes, I agree with @benwilson512. I think if you change it to the “fuller” syntax, include a match in the else block you should be good.

EDIT: Don’t forget to remove the comma after the last clause in the with block before do.

Also, the way you have it, if the call to Responder.Supervisor.respond(responder, {reaction, interface}) fails with an error, this will be handled outside of the function. I mean, this may be what you want, but perhaps we could get some comments from others on the various dynamics of this.

I personally have only seen bare return tuples in the do block, as opposed to another function call. This way, all error handling happens within your nice with statement cleanly.

yes, i can actually return a {:ok, something} when matching the result of the get_child.
Regarding the call to the Responder, this won’t error and it if happens, i’m not interested in
the error, at least not inside the with block.

Thanks for the replies,

1 Like