# UndefVarError when calling macro inside function

**URL:** <https://discourse.julialang.org/t/undefvarerror-when-calling-macro-inside-function/82320>\
**Category:** General Usage\
**Tags:** metaprogramming\
**Created:** [June 6, 2022, 3:25pm UTC](https://discourse.julialang.org/t/undefvarerror-when-calling-macro-inside-function/82320 "2022-06-06T15:25:49Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![luciano-drozda](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/luciano-drozda/32/33789_2.png) [@luciano-drozda](https://discourse.julialang.org/u/luciano-drozda)\
**Post date:** [June 6, 2022, 3:25pm UTC](https://discourse.julialang.org/t/undefvarerror-when-calling-macro-inside-function/82320/1 "2022-06-06T15:25:49Z")

</div>

It relates to [this post](https://discourse.julialang.org/t/macro-cannot-see-variables-in-calling-functions-scope/34806) but I’m still unclear about the correct use of `esc` in my case.

I’m willing to call a function `func` to all fields of a struct.

Here’s my macro :

```julia
macro call_on_fields(func, struct_)
  exprs = [:($struct_.$field) for field in fieldnames(eval(:(typeof($struct_))))]
  return :($func($(exprs...)))
end

```

Here’s the struct def :

```julia
mutable struct mystruct
  a :: Int
  b :: Int
end

```

When defining

```julia
function f()

  foo(args...) = sum(args)
  s = mystruct(1, 2)
  
  @call_on_fields foo s

end

```

the REPL says

```julia
ERROR: LoadError: UndefVarError: s not defined
Stacktrace:
 [1] top-level scope
   @ none:1
 [2] eval
   @ ./boot.jl:373 [inlined]
 [3] eval(x::Expr)
   @ Base.MainInclude ./client.jl:453
 [4] var"@call_on_fields"( __source__ ::LineNumberNode, __module__ ::Module, func::Any, struct_::Any)
   @ Main ./REPL[4]:2
in expression starting at REPL[5]:6

```

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [June 6, 2022, 3:32pm UTC](https://discourse.julialang.org/t/undefvarerror-when-calling-macro-inside-function/82320/2 "2022-06-06T15:32:41Z")

</div>

Not answering your question, but as a rule of thumb if you’re using `eval` inside a macro you’re likely doing something wrong, as using `eval` defeats the purpose of using a macro. Maybe you shouldn’t use a macro to start with.

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [June 6, 2022, 3:51pm UTC](https://discourse.julialang.org/t/undefvarerror-when-calling-macro-inside-function/82320/3 "2022-06-06T15:51:31Z")

</div>

And to answer your question, if I understand what you wanted to do, you can do

```julia
julia> macro call_on_fields(func, struct_)
         return :( $(esc(func))(getfield($(esc(struct_)), field) for field in fieldnames(typeof($(esc(struct_))))) )
       end
@call_on_fields (macro with 1 method)

julia> function f()

         foo = sum
         s = mystruct(1, 2)
         
         @call_on_fields foo s

       end
f (generic function with 1 method)

julia> f()
3

```

but again, this is more easily solved by using a plain function, rather than a macro.

---

<div class="post-metadata">

**Author:** ![luciano-drozda](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/luciano-drozda/32/33789_2.png) [@luciano-drozda](https://discourse.julialang.org/u/luciano-drozda)\
**Post date:** [June 6, 2022, 4:00pm UTC](https://discourse.julialang.org/t/undefvarerror-when-calling-macro-inside-function/82320/4 "2022-06-06T16:00:14Z")

</div>

Thanks @giordano. I was actually trying to find a way to do

`func(struct_) = func(struct_.field1, ..., struct_.fieldN)`.

I previously considered using array comprehension :

`func(struct_) = func( [struct_.field for field in fieldnames(typeof(struct_))]... )`

but it is **allocating memory**. That’s why I moved to metaprogramming.

Do you know of a non-allocating plain function that could do that ?

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [June 6, 2022, 4:04pm UTC](https://discourse.julialang.org/t/undefvarerror-when-calling-macro-inside-function/82320/5 "2022-06-06T16:04:25Z")

</div>

> [@luciano-drozda](#):
>
> but it is **allocating memory**. That’s why I moved to metaprogramming.

A macro does _ **not** _ change the fact that you’d allocate memory.

> [@luciano-drozda](#):
>
> Do you know of a non-allocating plain function that could do that ?

Don’t create a vector, use a [generator](https://docs.julialang.org/en/v1/manual/arrays/#Generator-Expressions). Which is what I did in the macro above.

```julia
julia> call_on_fields(f, s) = f(getfield(s, field) for field in fieldnames(typeof(s)))
call_on_fields (generic function with 1 method)

julia> call_on_fields(sum, mystruct(1, 2))
3

julia> @benchmark call_on_fields(sum, mystruct(1, 2))
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
 Range (min … max): 1.384 ns … 12.575 ns ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 1.432 ns ┊ GC (median): 0.00%
 Time (mean ± σ): 1.454 ns ± 0.437 ns ┊ GC (mean ± σ): 0.00% ± 0.00%

  ▃ ▄ █▆ ▁ ▂ ▂ ▁
  █▁█▄▁▁▁▁▁██▅▁▁▁▁█▁██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▃█ █
  1.38 ns Histogram: log(frequency) by time 1.66 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

```

The `call_on_fields` function above is the plain-function version of the macro in my previous message. Which one do you find more readable? 🙂

---

<div class="post-metadata">

**Author:** ![luciano-drozda](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/luciano-drozda/32/33789_2.png) [@luciano-drozda](https://discourse.julialang.org/u/luciano-drozda)\
**Post date:** [June 6, 2022, 4:12pm UTC](https://discourse.julialang.org/t/undefvarerror-when-calling-macro-inside-function/82320/6 "2022-06-06T16:12:37Z")

</div>

Wow, I didn’t know about generators. Really handful (and more readable than the macro, for sure 😁). One last question :

What if I want to do `call_on_fields(f, s, args...)` to call `f` not only to all fields of `s` but also to `args...` ?

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [June 6, 2022, 4:20pm UTC](https://discourse.julialang.org/t/undefvarerror-when-calling-macro-inside-function/82320/7 "2022-06-06T16:20:25Z")

</div>

Unless `f` is a linear operator, I’m afraid you’d have to create a vector which concatenates the fields of `s` with `args...`.

---

<div class="post-metadata">

**Author:** ![luciano-drozda](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/luciano-drozda/32/33789_2.png) [@luciano-drozda](https://discourse.julialang.org/u/luciano-drozda)\
**Post date:** [June 6, 2022, 4:25pm UTC](https://discourse.julialang.org/t/undefvarerror-when-calling-macro-inside-function/82320/8 "2022-06-06T16:25:37Z")

</div>

Hmm, I see. Thanks a lot for your help !
