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
Thanks,
Dragos
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
Thanks,
Dragos
Yes, it is. Can you post a code sample and the error message you are getting?
Yes!
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:
with(
# You can comment each line with the paren syntax
{:ok, bar} <- foo(),
{:ok, result} <- baz(bar)
) do
{:ok, result}
else
# To show that you can match on the error returned
{:error, reason} -> {:error, reason}
error -> {:error, inspect error}
end
There are a couple of points to remember:
with
block proper requires the arrows to point to the left. (This still trips me up sometimes).,
after the last with
block clause. (This also gets me if I’m copy/pasting and won’t compile)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.
Thanks,
Dragos
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,
Dragos