axelson
Weird/incorrect output from a with statement
This is a question that originated on the Elixir Slack from David Billskog and I have made only minor changes to the module.
Given this module:
defmodule Dummy do
def execute(x) do
with var_a <- get_a(x),
var_b <- get_b(x) do
IO.inspect(var_a, label: "a")
IO.inspect(var_b, label: "b")
IO.inspect({var_a, var_b})
if var_a != var_b do
IO.inspect(var_a, label: "a")
IO.inspect(var_b, label: "b")
IO.inspect({var_a, var_b})
end
end
end
defp get_a(x) do
IO.puts("get a")
if Enum.any?(x, & &1 == "A"), do: 3, else: 2
|> IO.inspect(label: "returning a")
end
defp get_b(x) do
IO.puts("get b")
if Enum.any?(x, & &1 == "B"), do: 2, else: 1
#|> IO.inspect(label: "returning b")
end
end
Dummy.execute(["A", "B"])
I get this unexpected output:
$ elixir sample.exs
get a
get b
a: 3
b: 2
{3, 2}
a: 3
b: 1
{3, 1}
Does anyone understand what is happening here? I don’t expect for the value of b to change within the if statement, and I do expect the returning a inspect to be printed. Tested on elixir 1.11.2-otp-23 and erlang 23.0.2.
Marked As Solved
josevalim
Thanks for the isolated case. I was able to convert it to Erlang:
-module(dummy).
-compile([no_auto_import]).
-export([execute/1]).
execute(_x@1) ->
_a@1 = get_a(_x@1),
_b@1 = get_b(_x@1),
erlang:display(_b@1),
case _a@1 /= _b@1 of
false ->
nil;
true ->
erlang:display(_b@1),
ok
end.
get_a(_x@1) ->
case _x@1 == <<"A">> of
false ->
2;
true ->
3
end.
get_b(_x@1) ->
case _x@1 == <<"A">> of
false ->
1;
true ->
2
end.
And I see the same issue. I will open up a bug report.
Also Liked
blackode
We can check how the code was developed by using quote do
quote do
if Enum.any?(x, & &1 == "A"), do: 3, else: 2
|> IO.inspect(label: "returning a")
end
|> Macro.to_string()
|> IO.puts()
That results to
if(Enum.any?(x, &(&1 == "A"))) do
3
else
2 |> IO.inspect(label: "returning a")
end
Which is always true in this case. So, it won’t enter into else
axelson
I’m still seeing the issue with elixir 1.11.2-otp-23 and erlang 23.1.5
al2o3cr
Can confirm - Axelson’s example works fine on my machine (OTP23.1 / Elixir 1.11.2) and this one fails.
A colleague tried @LostKobrakai’s example on 22.2.6 / 1.11.1 and it printed 2 three times, as expected…
al2o3cr
Poked at it a little more. Strange things are afoot - I get different results depending on what’s on a line that never executes (OTP 23.1 / Elixir 1.11.2):
defmodule Dummy do
def execute(x) do
with var_a <- get_a(x),
var_b <- get_b(x) do
IO.inspect(var_a, label: "a")
IO.inspect(var_b, label: "b")
IO.inspect({var_a, var_b})
if var_a != var_b do
IO.inspect(var_a, label: "a")
IO.inspect(var_b, label: "b")
IO.inspect({var_a, var_b})
end
end
end
defp get_a(x) do
IO.puts("get a")
if Enum.any?(x, & &1 == "A") do
3
else
2 |> IO.inspect(label: "nope")
end
end
defp get_b(x) do
IO.puts("get b")
if Enum.any?(x, & &1 == "B"), do: 2, else: 1
end
end
Dummy.execute(["A", "B"])
# prints
get a
get b
a: 3
b: 2
{3, 2}
a: 3
b: 2
{3, 2}
But commenting out |> IO.inspect(label: "nope") makes it do the {3,1} thing…
EDIT: bonus fact - the 1 has some kind of additional significance, changing it to anything else makes the bug stop happening on my machine.
dbi
I simplified the example to this and can still reproduce the odd print running macOS Catalina 10.15.7 using Elixir 1.11.2 and Erlang 23.1.5.
defmodule Dummy do
def execute(x) do
with a <- get_a(x), b <- get_b(x) do
IO.inspect(b)
if a != b do
IO.inspect(b)
end
end
end
defp get_a(x) do
if x == "A", do: 3, else: 2
end
defp get_b(x) do
if x == "A", do: 2, else: 1
end
end
Dummy.execute("A")
# 2
# 1
# 1
Simplifying it one step furter, replacing if x == "A", do: 3, else: 2 (that will always evaluate to true) into if true, do: 3, else: 2 will fix the unexpected output.
defmodule Dummy do
def execute(x) do
with a <- get_a(x), b <- get_b(x) do
IO.inspect(b)
if a != b do
IO.inspect(b)
end
end
end
defp get_a(x) do
if true, do: 3, else: 2
end
defp get_b(x) do
if x == "A", do: 2, else: 1
end
end
Dummy.execute("A")
# 2
# 2
# 2







