Skip to content

Instantly share code, notes, and snippets.

@iamvery
Last active June 20, 2016 13:08
Show Gist options
  • Save iamvery/a4349833ddb336bbd1dae36331c7c020 to your computer and use it in GitHub Desktop.
Save iamvery/a4349833ddb336bbd1dae36331c7c020 to your computer and use it in GitHub Desktop.
Interesting behavior with recursive macro in Elixir

ANSWERED!

Since a local variable head is defined in the quoted expression, the recursive call to the function overwrites the value of head in the expanded expression. This isn't a problem when quoting in the tail of the list , because head was already used to build as its head. :mindblown:

Here's the final implementation of recurse/1:

def recurse([h|t]) do
  t = recurse(t)
  quote do
    [unquote(h)|unquote(t)]
  end
end

🤔

Output as seen in code provided:

» mix test test/recursive_macro_test.exs
[3]
[2, 3]
[1, 2, 3]
.

Finished in 0.04 seconds (0.04s on load, 0.00s on tests)
1 test, 0 failures

Randomized with seed 9300

Output with implemention changed as commented:

» mix test test/recursive_macro_test.exs
[3]
[3, 3]
[3, 3, 3]


  1) test recursive macro (RecursiveMacroTest)
     test/recursive_macro_test.exs:23
     Assertion with == failed
     code: recursive([1, 2, 3]) == [1, 2, 3]
     lhs:  [3, 3, 3]
     rhs:  [1, 2, 3]
     stacktrace:
       test/recursive_macro_test.exs:24



Finished in 0.04 seconds (0.04s on load, 0.00s on tests)
1 test, 1 failure

Randomized with seed 812630
defmodule RecursiveMacroTest do
use ExUnit.Case
defmacro recursive(list) do
recurse(list)
end
defp recurse([]), do: []
defp recurse([h|t]) do
quote do
head = unquote(h)
# ---
[head|unquote(recurse(t))]
# why does changing the implementation to this break the tests?
#tail = unquote(recurse(t))
#[head|tail]
# here's another example that results in surprising behavior
# the problem is even exhibited when no local variables are affected
#unquote(recurse(t))
#[head|unquote(recurse(t))]
# ---
|> IO.inspect
end
end
test "recursive macro" do
assert recursive([1,2,3]) == [1,2,3]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment