I am trying to write a macro that may generate an assignment statement to modify one of its arguments (more precisely, modify the object the argument binds to). That argument will be a member of a mutable struct; which member will vary from call to call.
It seems that when I pass a struct member reference to a macro, Julia’s macro expansion assumes the argument is a global within the current module, not local to the scope of the calling function, which leads to a crash. What am I doing wrong?
Example:
mutable struct S x end
macro m(arg)
:( $arg = 1 )
end
function f()
@show @macroexpand @m(s)
@show @macroexpand @m(s.x)
s = S(1)
@m(s.x)
end
f()
When I run this, I get
#= Untitled-1:8 =# @macroexpand(#= Untitled-1:8 =# @m(s)) = :(var"#18#s" = 1)
#= Untitled-1:9 =# @macroexpand(#= Untitled-1:9 =# @m(s.x)) = :((Main.s).x = 1)
ERROR: LoadError: UndefVarError: s not defined
Stacktrace:
[1] f()
@ Main .\Untitled-1:12
[2] top-level scope
@ Untitled-1:15
in expression starting at Untitled-1:15
The first @macroexpand output line looks like I would want it to look: it assigns via a temporary alias with no scope qualifications. The second instead refers directly to Main.s, which does not exist. I just want it to refer to s, the calling function’s local variable.
Background: my real purpose is to cleanly and efficiently implement what is in spirit a “!” function that may in some cases produce output exactly equal to one of its inputs. In those cases, I do not want to copy the input matrix into the provided output matrix, but rather change the binding of the caller’s name for the output matrix to the input matrix. A macro seems like the Julianic way to do this, but maybe not?