evaluates to [[nil]], shouldn’t the “equivalent” expression
for _ <- 1..3, uniq: true do
for _ <- 1..3, uniq: true do
nil
end
end
also evaluate to [[nil]]?
Instead I get the following exception:
** (MatchError) no match of right hand side value: {[nil], %{nil: true}}
The following iex session also illustrates this question:
Interactive Elixir (1.13.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> for _ <- 1..3 do
...(1)> nil
...(1)> end
[nil, nil, nil]
iex(2)> for _ <- 1..3, uniq: true do
...(2)> nil
...(2)> end
[nil]
iex(3)> for _ <- 1..3 do
...(3)> [nil]
...(3)> end
[[nil], [nil], [nil]]
iex(4)> for _ <- 1..3, uniq: true do
...(4)> [nil]
...(4)> end
[[nil]]
iex(5)> for _ <- 1..3 do
...(5)> for _ <- 1..3, uniq: true do
...(5)> nil
...(5)> end
...(5)> end
[[nil], [nil], [nil]]
iex(6)> for _ <- 1..3, uniq: true do
...(6)> for _ <- 1..3, uniq: true do
...(6)> nil
...(6)> end
...(6)> end
** (MatchError) no match of right hand side value: {[nil], %{nil: true}}
(stdlib 3.17) erl_eval.erl:450: :erl_eval.expr/5
(stdlib 3.17) erl_eval.erl:123: :erl_eval.exprs/5
(elixir 1.13.1) lib/enum.ex:4136: Enum.reduce_range/5
iex(6)>
I disassembled the generated BEAM code and there’s something odd. Here’s the code:
{:module, Foo, bytecode, _} = defmodule Foo do
def foo do
for _ <- 1..3, uniq: true do
for _ <- 1..3, uniq: true do
nil
end
end
end
end
:beam_disasm.file(bytecode) |> IO.inspect(limit: :infinity)
Trimming out the unimportant bits (module_info etc), gives this output (annotations after # far to the right):
The inner fun seems to be checking the value of the outer loop’s accumulators versus the inner loop (and if they disagree, jumps to label 23 and throws a bad match error).
I don’t see any corresponding structure in the relevant-looking bit of the compiler:
Maybe the optimizer is breaking the nested loops somehow?