# Improving macro programming

**URL:** <https://discourse.julialang.org/t/improving-macro-programming/42755>\
**Category:** General Usage\
**Tags:** macros, metaprogramming\
**Created:** [July 8, 2020, 8:31pm UTC](https://discourse.julialang.org/t/improving-macro-programming/42755 "2020-07-08T20:31:33Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Joris\_Pinkse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joris_pinkse/32/216398_2.png) [@Joris\_Pinkse](https://discourse.julialang.org/u/Joris_Pinkse)\
**Post date:** [July 8, 2020, 8:31pm UTC](https://discourse.julialang.org/t/improving-macro-programming/42755/1 "2020-07-08T20:31:33Z")

</div>

I’m learning macro programming and as an experiment I programmed up a macro version of map!. It works, but seems clunky with all the escapes. What would be a more elegant way of programming the macro? I can define local variables, but there seems to be a (small) performance penalty. Thanks!

```julia
macro map(f,y,x)
    quote 
        for j = 1:length($(esc(x)))
            $(esc(y))[j] = $(esc(f))($(esc(x))[j]) 
        end
    end
end

function tryme()
    x = randn(10)
    y = zeros(10)
    @map( sin, y, x )
    println(y)
end

tryme()
```

---

<div class="post-metadata">

**Author:** ![simeonschaub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simeonschaub/32/216566_2.png) [@simeonschaub](https://discourse.julialang.org/u/simeonschaub)\
**Post date:** [July 8, 2020, 8:45pm UTC](https://discourse.julialang.org/t/improving-macro-programming/42755/2 "2020-07-08T20:45:01Z")

</div>

The most elegant way would be to define it as a function instead 😄. Macros are only really for rewriting syntax, for stuff like this a function is a more powerful, because you have access to type information, can do dispatch, use it in higher order function and makes use of dot broadcasting. You also avoid problems related to scope. Only use a macro, if there’s really no way to do it in a function, because you are doing syntactic transformations.  
That said, in your case, you could just call `esc` on the final expression instead of each variable on its own.

---

<div class="post-metadata">

**Author:** ![Joris\_Pinkse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joris_pinkse/32/216398_2.png) [@Joris\_Pinkse](https://discourse.julialang.org/u/Joris_Pinkse)\
**Post date:** [July 8, 2020, 8:47pm UTC](https://discourse.julialang.org/t/improving-macro-programming/42755/3 "2020-07-08T20:47:26Z")

</div>

Thanks @simeonschaub Yes, this is for me getting familiar with macros only. What would the final expression look like? (I had tried that and kept getting errors.)

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [July 8, 2020, 8:57pm UTC](https://discourse.julialang.org/t/improving-macro-programming/42755/4 "2020-07-08T20:57:16Z")

</div>

Here’s how I would write that macro:

```julia
macro map(f,y,x)
    out = quote 
        for j = 1:length(x)
            $y[j] = $f($x[j]) 
        end
    end
    esc(out)
end

```

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [July 8, 2020, 9:12pm UTC](https://discourse.julialang.org/t/improving-macro-programming/42755/5 "2020-07-08T21:12:42Z")

</div>

Note that using `esc` like this will cause some interesting and hard-to-find bugs.

For example, the following code, using a function to implement `map`, works fine:

```julia
julia> function map_function(f, y, x)
         for j in 1:length(x)
           y[j] = f(x[j])
         end
       end
map_function (generic function with 1 method)

julia> function do_stuff()
         length = 3
         x = [i for i in 1:length]
         y = zeros(length)
         map_function(sin, y, x)
         @show y
       end
do_stuff (generic function with 1 method)

julia> do_stuff()
y = [0.8414709848078965, 0.9092974268256817, 0.1411200080598672]
3-element Array{Float64,1}:
 0.8414709848078965
 0.9092974268256817
 0.1411200080598672

```

But what happens if we use the `esc()`-ed macro?

```julia
julia> macro map(f,y,x)
           out = quote 
               for j = 1:length(x)
                   $y[j] = $f($x[j]) 
               end
           end
           esc(out)
       end
@map (macro with 1 method)

julia> function do_stuff()
         length = 3
         x = [i for i in 1:length]
         y = zeros(length)
         @map(sin, y, x)
         @show y
       end
do_stuff (generic function with 1 method)

julia> do_stuff()
ERROR: MethodError: objects of type Int64 are not callable
Stacktrace:
 [1] macro expansion at ./REPL[7]:3 [inlined]
 [2] do_stuff() at ./REPL[8]:5

```

Oops.

This is exactly why you should _not_ blindly `esc()` everything returned by the macro. What’s happening here is that there is a local variable called `length`. By calling `esc()` in the macro, you are saying “every symbol returned by this macro should refer to whatever symbol is in the scope where the macro was called”. That means that when the code returned by the macro does `length(x)`, it tries to call the local variable `length` as if it were a function.

Instead, you must only escape the inputs provided by the user (`f`, `y`, and `x` in this case). Doing so fixes the issue:

```julia
julia> macro map(f,y,x)
           quote 
               for j = 1:length($(esc(x)))
                   $(esc(y))[j] = $(esc(f))($(esc(x))[j]) 
               end
           end
       end
@map (macro with 1 method)

julia> function do_stuff()
         length = 3
         x = [i for i in 1:length]
         y = zeros(length)
         @map(sin, y, x)
         @show y
       end
do_stuff (generic function with 1 method)

julia> do_stuff()
y = [0.8414709848078965, 0.9092974268256817, 0.1411200080598672]
3-element Array{Float64,1}:
 0.8414709848078965
 0.9092974268256817
 0.1411200080598672

```

There’s no avoiding the fact that doing `esc` correctly creates some extra noise, unfortunately. Subtleties of macro hygiene are one of many reasons to prefer writing functions unless you are doing something that can only be handled by a macro.

---

<div class="post-metadata">

**Author:** ![Joris\_Pinkse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joris_pinkse/32/216398_2.png) [@Joris\_Pinkse](https://discourse.julialang.org/u/Joris_Pinkse)\
**Post date:** [July 8, 2020, 9:17pm UTC](https://discourse.julialang.org/t/improving-macro-programming/42755/6 "2020-07-08T21:17:13Z")

</div>

Thanks @rdeits ok, so you’re saying that what I had was the safest thing one could do.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [July 8, 2020, 9:28pm UTC](https://discourse.julialang.org/t/improving-macro-programming/42755/7 "2020-07-08T21:28:33Z")

</div>

Yep, this is totally right. I tend to err on the side of yolo-ing it with macro hygiene.

One option @Joris_Pinkse if you want the better namespacing and cleaner syntax is just to esc the variables before you interpolate them:

```julia
macro map(f,y,x)
    ef, ey, ex = esc.((f, y, x))
    quote 
        for j = 1:length($ex)
            $ey[j] = $ef($ex[j]) 
        end
    end
end

```

or even

```julia
macro map(f,y,x)
    f, y, x = esc.((f, y, x))
    quote 
        for j = 1:length($x)
            $y[j] = $f($x[j]) 
        end
    end
end

```

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [July 8, 2020, 9:45pm UTC](https://discourse.julialang.org/t/improving-macro-programming/42755/8 "2020-07-08T21:45:33Z")

</div>

That’s a neat pattern, and you can even make it fancier with macros in your macro (so you can macro while you macro…):

```julia
julia> macro escape(args...)
         Expr(:block, [:($(esc(arg)) = esc($(esc(arg)))) for arg in args]...)
       end
@escape (macro with 1 method)

julia> macro map(f, y, x)
         @escape f x y
         quote
           for j = 1:length($x)
             $y[j] = $f($x[j])
           end
         end
       end
@map (macro with 1 method)

```

---

<div class="post-metadata">

**Author:** ![Joris\_Pinkse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joris_pinkse/32/216398_2.png) [@Joris\_Pinkse](https://discourse.julialang.org/u/Joris_Pinkse)\
**Post date:** [July 8, 2020, 10:41pm UTC](https://discourse.julialang.org/t/improving-macro-programming/42755/9 "2020-07-08T22:41:26Z")

</div>

Thanks @Mason What’s the advantage of escaping over defining local variables inside the macro?

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [July 8, 2020, 10:59pm UTC](https://discourse.julialang.org/t/improving-macro-programming/42755/10 "2020-07-08T22:59:07Z")

</div>

> [@rdeits](#):
>
> it tries to call the local variable `length` as if it were a function.

While escaping individual variables is the right thing, another possible solution here is to interpolate the function:

```julia
julia> macro map(f,y,x)
           quote 
               for j = 1:$length(x)
                   $y[j] = $f($x[j]) 
               end
           end |> esc
       end

```

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [July 9, 2020, 10:10am UTC](https://discourse.julialang.org/t/improving-macro-programming/42755/11 "2020-07-09T10:10:26Z")

</div>

> [@mcabbott](#):
>
> interpolate the function

I think it is worth emphasizing that this will interpolate the generic function `Base.length`, not the symbol. Eg

```julia
julia> ex = @macroexpand @map(sin, y, x)
quote
    #= REPL[19]:3 =#
    for j = 1:(length)(x)
        #= REPL[19]:4 =#
        y[j] = sin(x[j])
    end
end

julia> ex.args[2].args[1].args[2].args[3].args[1] === length
true

```

Now in this case this is not problematic because `length` is in `Base`. But consider

```julia
macro map(f,y,x)
    quote
        for j = 1:$mylen(x)
            $y[j] = $f($x[j])
        end
    end |> esc
end

function do_stuff()
    x = [i for i in 1:5]
    y = zeros(5)
    @map(sin, y, x)
    @show y
end

mylen(x) = length(x) # ORDER MATTERS

```

This errors because `mylen` is not defined at macroexpansion time, but works fine with the solutions that escape everything carefully (the example is contrived, but could easily happen, especially with functions scattered around in modules). Because of this, and the fact that it is easy to forget interpolating something in more complex code, I would advocate those solutions.

Macros in Julia have a lot of tricky corner cases. Despite loving them in Common Lisp, I am still trying to avoid Julia macros whenever I can. Fortunately, Julia is so powerful in other ways that this is easy to do.
