# Foreach for macros?

**URL:** <https://discourse.julialang.org/t/foreach-for-macros/57678>\
**Category:** General Usage\
**Created:** [March 22, 2021, 1:03am UTC](https://discourse.julialang.org/t/foreach-for-macros/57678 "2021-03-22T01:03:48Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![turquoise](https://avatars.discourse-cdn.com/v4/letter/t/71e660/32.png) [@turquoise](https://discourse.julialang.org/u/turquoise)\
**Post date:** [March 22, 2021, 1:03am UTC](https://discourse.julialang.org/t/foreach-for-macros/57678/1 "2021-03-22T01:03:48Z")

</div>

I am trying to define a foreach macro that works with macros. That is:

```julia
@foreach @amacro expr1 expr2 ... 

```

Should give

```julia
@amacro(expr1) @amacro(expr2) ... 

```

I couldn’t make this work even with a single expr input. I defined a trial macro:

```julia
macro trial(expr) 
   quote 
        println($(QuoteNode(expr))) 
   end
end

```

@trial works as expected. Then i tried to implement foreach macro for a single expr input:

```julia
macro foreach1(amacro,expr)
   quote
       $(amacro)($(expr))
   end
end

```

This doesn’t work. I also tried to construct the expression using :macrocall directly but that also didn’t work:

```julia
macro foreach(amacro,expr)       
    Expr(:macrocall,amacro,LineNumberNode((0),esc(expr))
end

```

This also didn’t work.

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [March 22, 2021, 2:35am UTC](https://discourse.julialang.org/t/foreach-for-macros/57678/2 "2021-03-22T02:35:12Z")

</div>

This should do it.

```julia
julia> function foreach_helper(x)
           mname = x.args[1]
           t = x.args[3:end]
           out = quote end
           for ti in t
               out = quote
                   $out
                   $(Expr(:macrocall, mname, LineNumberNode(0), ti))
               end
           end
           return out
       end

julia> macro t(x)
       println(x)
       esc(x)
       end
@t (macro with 1 method)

julia> @foreach @t 1 2;
1
2

```

I have no clue how to do `LineNumberNode` responsibly though. This is all kind of a black box for me.

---

<div class="post-metadata">

**Author:** ![turquoise](https://avatars.discourse-cdn.com/v4/letter/t/71e660/32.png) [@turquoise](https://discourse.julialang.org/u/turquoise)\
**Post date:** [March 22, 2021, 5:29am UTC](https://discourse.julialang.org/t/foreach-for-macros/57678/3 "2021-03-22T05:29:44Z")

</div>

Thanks a lot!

Your solution wasn’t working if macro was called from another module. So I came up with the following solution. This version works with macros with multiple arguments. For example, if @a\_macro takes two arguments:

```julia
@my_foreach @a_macro [a_1,a_2,...,a_n] [b_1,b_2,...,b_n]

```

turns into

```julia
@a_macro a_1 b_1 ; @a_macro a_2 b_2; ...; @a_macro a_n b_n

```

```julia
function my_foreach_helper(x,calling_module)
    macro_name = x.args[1]
    input_lists = map(expr->expr.args,x.args[3:end])
    ex = quote end
    while !isempty(input_lists[1])
          inputs = map(pop!,input_lists)
          macro_expr = Expr(:macrocall,Expr(:.,calling_module,QuoteNode(macro_name)),LineNumberNode(0),inputs...)
          ex = quote
                  $(ex);
                  $(macro_expr);
               end
    end 
    ex
end 

macro my_foreach(x)
    my_foreach_helper(x, __module__ )
end

```

I wanted to ask whether you have any suggestions as I am not very comfortable with escaping manually. On the other hand, I don’t want to escape all of the expressions as there may have situations where it actually changes something.

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [March 22, 2021, 2:33pm UTC](https://discourse.julialang.org/t/foreach-for-macros/57678/4 "2021-03-22T14:33:02Z")

</div>

Your problem is probably solved by doing

```julia
macro my_foreach(x)
   esc( my_foreach_helper(x))
end

```

you missed the `esc` which might be affecting things.

---

<div class="post-metadata">

**Author:** ![turquoise](https://avatars.discourse-cdn.com/v4/letter/t/71e660/32.png) [@turquoise](https://discourse.julialang.org/u/turquoise)\
**Post date:** [March 22, 2021, 7:09pm UTC](https://discourse.julialang.org/t/foreach-for-macros/57678/5 "2021-03-22T19:09:12Z")

</div>

Thanks, my solution was working, I just wanted to ask whether you had any suggestions to improve it.

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [March 22, 2021, 7:12pm UTC](https://discourse.julialang.org/t/foreach-for-macros/57678/6 "2021-03-22T19:12:13Z")

</div>

yes, you definitely need the `esc`. It ensures proper macro hygiene and will get rid of your issue about the modules.

---

<div class="post-metadata">

**Author:** ![turquoise](https://avatars.discourse-cdn.com/v4/letter/t/71e660/32.png) [@turquoise](https://discourse.julialang.org/u/turquoise)\
**Post date:** [March 22, 2021, 7:18pm UTC](https://discourse.julialang.org/t/foreach-for-macros/57678/7 "2021-03-22T19:18:36Z")

</div>

Actually, my solution is working without esc when calling from another module. **module** is the module of calling scope and I pass this to the helper function. It is used while constructing the macro expression:

```julia
Expr(:macrocall,Expr(:.,calling_module,QuoteNode(macro_name)),LineNumberNode(0),inputs...)

```

I have never seen

```julia
Expr(:.,calling_module,QuoteNode(macro_name))

```

anywhere. I found by examining dump(: (@m.amacro x)). It seems to be enough to escape hygiene.

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [March 22, 2021, 7:21pm UTC](https://discourse.julialang.org/t/foreach-for-macros/57678/8 "2021-03-22T19:21:50Z")

</div>

Will this work inside functions though? With only local variables?

---

<div class="post-metadata">

**Author:** ![turquoise](https://avatars.discourse-cdn.com/v4/letter/t/71e660/32.png) [@turquoise](https://discourse.julialang.org/u/turquoise)\
**Post date:** [March 22, 2021, 9:43pm UTC](https://discourse.julialang.org/t/foreach-for-macros/57678/9 "2021-03-22T21:43:15Z")

</div>

You are right, it wasn’t working. The following version seems to work.

```julia
module m 

function foreach(x,calling_scope)
      macro_name = x.args[1]
      input_lists = map(expr->expr.args,x.args[3:end])
      ex = Expr(:block)
      while !isempty(input_lists[1])
            inputs = map(pop!,input_lists)
            inputs = esc.(inputs)
            macro_expr = Expr(:macrocall,Expr(:.,calling_scope,QuoteNode(macro_name)),LineNumberNode(0),inputs...)
            pushfirst!(ex.args,macro_expr)
      end
      push!(ex.args,:(return nothing))
      ex
end 
       
macro foreach(x)
      foreach(x, __module__ )
end

end

```

The difference is, I also escaped the inputs (note that `esc(inputs)` doesn’t work, you need to broadcase it). To test it, I defined the nif macro:

```julia
macro nif(expr,pos,zero,negative)
           quote 
               if $(esc(expr)) > 0 
                   $(esc(pos))
               elseif $(esc(expr)) == 0
                   $(esc(zero))
               else 
                   $(esc(negative))
               end
           end
       end

```

Then:

```julia
let 
     x = 1 
     y = -1
     @m.foreach @nif [x,y] [println("x-positive"),println("y-positive")] [println("x-zero"),println("y-zero")] [println("x-negative"),println("y-negative")] 
end

```

prints

```julia
x-positive
y-negative

```

There is something tricky going on I believe. It seems that the input macro (here it is the @nif), thinks that calling scope is the module m. Therefore, you need to escape the inputs in @foreach, before supplying them to @nif.

---

<div class="post-metadata">

**Author:** ![turquoise](https://avatars.discourse-cdn.com/v4/letter/t/71e660/32.png) [@turquoise](https://discourse.julialang.org/u/turquoise)\
**Post date:** [March 22, 2021, 11:16pm UTC](https://discourse.julialang.org/t/foreach-for-macros/57678/10 "2021-03-22T23:16:59Z")

</div>

This wasn’t working if the input macro was called from a third module. Now I fixed that also. Escaping everything is much simpler but this was really instructive. One needs to obtain the module the input macro is defined in correctly and this is not straightforward because `@a_macro`, `@a_module.amacro` and `@another_module.a_module.a_macro` are parsed differently. The inner function `make_input_module_and_macro_name` returns the module input macro is defined and the name of the input macro.

```julia
#= escape manually 
 The function make_input_module_and_macro_name return (input_module,macro_name) if is_macro_name_found is false and returns input_module if is_macro_name_found is true.
=#
function foreach_helper(x,calling_scope)
           function make_input_module_and_macro_name(tree::Expr,is_macro_name_found)
               if is_macro_name_found
                   make_input_module_and_macro_name(tree.args[2],true)
               else 
                   (make_input_module_and_macro_name(tree.args[1],true), eval(tree.args[2]))
               end
           end
           make_input_module_and_macro_name(tree::Symbol,is_macro_name_found) = is_macro_name_found ? tree : (calling_scope,tree)
           make_input_module_and_macro_name(tree::QuoteNode,is_macro_name_found) = eval(tree)    
           input_module, macro_name = make_input_module_and_macro_name(x.args[1],false)
           input_lists = map(expr->expr.args,x.args[3:end])
           ex = Expr(:block)
           while !isempty(input_lists[1])
                 inputs = map(pop!,input_lists)
                 inputs = esc.(inputs)
                 macro_expr = Expr(:macrocall,Expr(:.,calling_scope,QuoteNode(macro_name)),LineNumberNode(0),inputs...)
                 pushfirst!(ex.args,macro_expr)
           end
           ex
       end
macro foreach(x)
    foreach_helper(x, __module__ )
end

```

Now one can define `@foreach` and the input macro in different modules and call them in a local scope (say let block) from a third module.
