I notice that when there is any variable in the iex scope the previous stops working
iex(2)> x = 1
1
iex(3)> quote do
...(3)> defmodule Bar do
...(3)> 1+2
...(3)> end
...(3)> end |> Macro.expand(__ENV__) |> Code.compile_quoted
warning: variable "x" does not exist and is being expanded to "x()", please use parentheses to remove the ambiguity or change the var
iable name
nofile
** (CompileError) nofile: undefined function x/0
...
Why did the variable screw up everything? I noticed that the variable x was actually in the expanded AST…
Newbie question: I would expect the expanded AST to only contain variables and values related to the source code which in my example is mainly 1 + 2. I don’t understand why is the variable x is the context involved?
For example in this scenario the variable x is not included
iex> quote do
...> 1+2
...> end |> Macro.expand(__ENV__) |> Code.eval_quoted()
{3, []}
defmodule is special in that bindings that exist in its outer scope get brought in at ‘module level’. Specifically when you defmodule it makes an alias in its outer scope (that is why you can call an inner module’s name directly without needing to fully scope it), then it calls the internal function that actually compiles a module of :elixir_module.compile(TheModuleNameHere, moduleASTHere, outerBindingsHere, environment). The defmodule macro you can see its source at:
But in essence it grabs active bindings from its parent environment (emulated in my macro calls in iex above via the Macro.expand(__ENV__) call) and adds them to the function that compiles the module so that it can resolve calls to those outer bindings.