Sorry if this has already been asked. Searching the forum for var! gives me all the posts with word var. Made it hard to narrow it down.
Struggling with writing a macro that reads a variable out of the caller's context and returns it from a function. Here's the simplest form of the problem I could think of:
defmodule MyUnhygienicMacros do
  defmacro create_get_function do
    quote do
      def get_my_var do
        var!(my_var)
      end
    end
  end
end
defmodule Caller do
  require MyUnhygienicMacros
  my_var = "happy"
  MyUnhygienicMacros.create_get_function()
end
The goal would be to see this when I run an iex session:
$ Caller.get_my_var()
"happy"
But this does not compile. The caller's my_var goes unused too.
The CompileError expected "my_var" to expand to an existing variable or be part of a match.
I've read McCord's metaprogramming book, this blog post (https://www.theerlangelist.com/article/macros_6) and many others. Seems like it should work, but I just can't figure out why it won't..
                        
Kernel.var!/2macro does not do what you think it does.The sole purpose of
var!/2is to mark the variable off the macro hygiene. That means, usingvar!/2one might change the value of the variable in the outer (in regard to the current context) scope. In your example, there are two scopes (defmacro[create_get_function]anddef[get_my_var]) to bypass, which is whymy_vardoes not get through.The whole issue looks like an XY-Problem. It looks like you want to declare kinda compile-time variable and modify it all way through the module code. For this purpose we have module attributes with
accumulate: true.If you want to simply use this variable in
create_get_function/0, justunquote/1it. If you want to accumulate the value, use module attributes. If you still ultimately want to keep it your way, passing the local compile-time variable through, break hygiene twice, for both scopes.Please note, that unlike you might have expected, the code above still prints
modified?: "happy"during compile-time. This happens becausevar!(my_var_inner) = 42call would be held until runtime, and bypassing macro hygiene here would be a no-op.