Macro cannot see variables in calling function's scope

Scenario:
A variable exists in the (local) scope of a function. func1 calls macro passing it the variable as a parameter. But, the macro can’t see the variable. I have run into this issue when migrating code from version 0.4.6 to 1.3.0.
The example code below works in 0.4.6. How can I get back the intended behavior in 1.3.0?

Example:
Essentially, a macro and a function are defined in a file D:\test.jl as below:

macro remove(array, index) 
     quote
       temp = getindex($array, $index) 
       setindex!($array, 0, $index) 
       temp     
     end 
end


function func1(matrix)
	# do something with the matrix
	location = 5
	removed_value = @remove matrix location
	println(matrix)
	removed_value
end

Now on the REPL (Julia 1.3.1), func1() is called:

julia> include("d:\\test.jl")
func1 (generic function with 1 method)

julia> B=rand(3,3)
3x3 Array{Float64,2}:
 0.672525   0.185575  0.519812
 0.0657534  0.824389  0.997873
 0.298938   0.879692  0.544854

julia> func1(B)
ERROR: UndefVarError: matrix not defined
Stacktrace:
 [1] func1(::Array{Float64,2}) at D:\test.jl:3
 [2] top-level scope at REPL[6]:1

However, the same code works, as intended, on Julia 0.4.6:

julia> include("d:\\test.jl")
func1 (generic function with 1 method)

julia>  B=rand(3,3)
3x3 Array{Float64,2}:
 0.31986    0.0326584  0.459205
 0.0532861  0.613278   0.954343
 0.889699   0.195983   0.969102

julia> func1(B)
[0.3198596980916635 0.03265841972646388 0.45920484654949445
 0.05328610654424648 0.0 0.9543434785771836
 0.8896992715733547 0.19598268732691326 0.9691021279154293]
0.6132781328188774

Having read Julia’s docs on “Scope of Variables” and posts on changes in Scope behavior from version 1.0 … I am still unable to figure out what I am doing wrong.

1 Like

You are missing the esc function.

julia> @macroexpand @remove matrix location
quote
    #= REPL[52]:3 =#
    var"#9#temp" = Main.getindex(Main.matrix, Main.location)
    #= REPL[52]:4 =#
    Main.setindex!(Main.matrix, 0, Main.location)
    #= REPL[52]:5 =#
    var"#9#temp"
end

julia> macro remove2(array, index) 
            esc(quote
              temp = getindex($array, $index) 
              setindex!($array, 0, $index) 
              temp     
            end)
       end
@remove2 (macro with 1 method)

julia> @macroexpand @remove2 matrix location
quote
    #= REPL[57]:3 =#
    temp = getindex(matrix, location)
    #= REPL[57]:4 =#
    setindex!(matrix, 0, location)
    #= REPL[57]:5 =#
    temp
end
5 Likes

The relevant part of the docs is Macro Hygiene.

3 Likes

@GunnarFarneback: Thank you! It works now.

@Sukera: That portion of the docs appears to be written well and to-the-point for an intermediate user. I feel that working of the powerful Metaprogramming features will become accessible to a wider audience as the docs refine with time.

Metaprogramming really should only be used if it’s absolutely necessary and by someone more familiar with the language than a beginner. It’s a powerful technique, but because of that it takes some skill to judge when it’s the right tool for the job.

2 Likes