# Variables in macro definition

**URL:** <https://discourse.julialang.org/t/variables-in-macro-definition/2307>\
**Category:** General Usage\
**Tags:** macros, metaprogramming\
**Created:** [February 25, 2017, 10:28pm UTC](https://discourse.julialang.org/t/variables-in-macro-definition/2307 "2017-02-25T22:28:35Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![gasagna](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gasagna/32/1275_2.png) [@gasagna](https://discourse.julialang.org/u/gasagna)\
**Post date:** [February 25, 2017, 10:28pm UTC](https://discourse.julialang.org/t/variables-in-macro-definition/2307/1 "2017-02-25T22:28:35Z")

</div>

I am developing a macro that lets me write sums in a nice way. A trivial example is:

```julia
# some array
x = [1, 2, 3]

# calculate sum
s = @Σ i=1:3 x[i]

```

I have tried this:

```julia
module MyModule

export @Σ

# fancy macro to write sums
macro Σ(limits, expr)
    index = limits.args[1]
    start, stop = limits.args[2].args
    quote
       local out = $(_indexreplace(index, start, expr))
       for i = ($start+1):$stop
           out += $(_indexreplace(index, :i, expr))
       end
       out 
    end
end

# replace the previous index with the loop index inside the macro
_indexreplace(old, new, e::Expr) = Expr(e.head, (_indexreplace(old, new, sub) for sub in e.args)...)
_indexreplace(old, new, e::Symbol) = e == old ? new : e

end

# tests
using MyModule

x = [1, 2, 3]

println(macroexpand(:(@Σ i=1:3 x[i])))

@Σ i=1:3 x[i]

```

Running this gives me

```julia
begin # /Users/davide/Research/RMCO/julia/LorenzRMCO/test/test_sums.jl, line 10:
    local #2#out = (MyModule.x)[1] # /Users/davide/Research/RMCO/julia/LorenzRMCO/test/test_sums.jl, line 11:
    for #3#i = 1 + 1:3 # /Users/davide/Research/RMCO/julia/LorenzRMCO/test/test_sums.jl, line 12:
        #2#out += (MyModule.x)[#3#i]
    end # /Users/davide/Research/RMCO/julia/LorenzRMCO/test/test_sums.jl, line 14:
    #2#out
end
ERROR: LoadError: UndefVarError: x not defined
 in macro expansion; at /Users/davide/Research/RMCO/julia/LorenzRMCO/test/test_sums.jl:10 [inlined]
 in anonymous at ./<missing>:?
 in include_from_node1(::String) at ./loading.jl:488
 in include_from_node1(::String) at /Users/davide/Software/julia-0.5/usr/lib/julia/sys.dylib:?
 in process_options(::Base.JLOptions) at ./client.jl:262
 in _start() at ./client.jl:318
 in _start() at /Users/davide/Software/julia-0.5/usr/lib/julia/sys.dylib:?
while loading /Users/davide/Research/RMCO/julia/LorenzRMCO/test/test_sums.jl, in expression starting on line 28

```

which results in an error because the macro code is calling `MyModule.x` which is not what I want. I have tried escaping the expression with `esc`, as discussed in the manual, but I can’t seem to find a solution to this. Any help is appreciated.

Thanks,

Davide

---

<div class="post-metadata">

**Author:** ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)\
**Post date:** [February 25, 2017, 10:44pm UTC](https://discourse.julialang.org/t/variables-in-macro-definition/2307/2 "2017-02-25T22:44:43Z")

</div>

In general, you must escape all the user input once and exactly once.

Example

```julia
julia> macro Σ(limits::Expr, expr)
           @assert limits.head === :(=)
           @assert length(limits.args) == 2
           index = limits.args[1]
           @assert Meta.isexpr(limits.args[2], :(:))
           start, stop = limits.args[2].args
           quote
               local _start = $(esc(start))
               local _stop = $(esc(stop))
               $(esc(index)) = _start
               local out = $(esc(expr))
               for $(esc(index)) in (_start + 1):_stop
                   out += $(esc(expr))
               end
               out
           end
       end
@Σ (macro with 1 method)

julia> f(x) = @Σ i=1:3 x[i]
f (generic function with 1 method)

julia> f([1, 2, 3])
6

```

---

<div class="post-metadata">

**Author:** ![RaulDurand](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rauldurand/32/2044_2.png) [@RaulDurand](https://discourse.julialang.org/u/RaulDurand)\
**Post date:** [February 26, 2017, 4:15am UTC](https://discourse.julialang.org/t/variables-in-macro-definition/2307/3 "2017-02-26T04:15:30Z")

</div>

What about  
`sum(x[i] for i=1:3)`

---

<div class="post-metadata">

**Author:** ![gasagna](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gasagna/32/1275_2.png) [@gasagna](https://discourse.julialang.org/u/gasagna)\
**Post date:** [February 26, 2017, 11:03am UTC](https://discourse.julialang.org/t/variables-in-macro-definition/2307/4 "2017-02-26T11:03:02Z")

</div>

I also tried the generator approach, but it can be much slower than expanding the body directly into a for loop. See this:

```julia
module MyModule

export @Σ

# fancy macro to write sums
macro Σ(limits, expr)
    @assert limits.head === :(=)
    @assert length(limits.args) == 2
    index = limits.args[1]
    start, stop = limits.args[2].args
    quote
       local _start = $(esc(start))
       local _stop = $(esc(stop))
       $(esc(index)) = _start
       local out = $(esc(expr))
       for $(esc(index)) = (_start+1):_stop
           out += $(esc(expr))
       end
       out 
    end
end

end

using MyModule
using BenchmarkTools

N = 400
x = randn(N, N)

fun1(x, j, N) = @Σ i=1:N x[i, j]
fun2(x, j, N) = sum(x[i, j] for i=1:N)

println(@benchmark fun1($x, 2, N))
println(@benchmark fun2($x, 2, N))

```

On my machine I get

```julia
Trial(320.763 ns)
Trial(5.931 μs)

```

that is twenty times faster.
