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!/2
macro does not do what you think it does.The sole purpose of
var!/2
is to mark the variable off the macro hygiene. That means, usingvar!/2
one 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_var
does 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/1
it. 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) = 42
call would be held until runtime, and bypassing macro hygiene here would be a no-op.