# Evaluate symbol passed to macro

**URL:** <https://discourse.julialang.org/t/evaluate-symbol-passed-to-macro/124810>\
**Category:** General Usage\
**Created:** [January 15, 2025, 9:06pm UTC](https://discourse.julialang.org/t/evaluate-symbol-passed-to-macro/124810 "2025-01-15T21:06:35Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Mikhail\_Kagalenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikhail_kagalenko/32/13257_2.png) [@Mikhail\_Kagalenko](https://discourse.julialang.org/u/Mikhail_Kagalenko)\
**Post date:** [January 15, 2025, 9:06pm UTC](https://discourse.julialang.org/t/evaluate-symbol-passed-to-macro/124810/1 "2025-01-15T21:06:35Z")

</div>

I have a macro that builds some `Expr` from a string passed to it, a simplified MWE is

```julia
module MyExpr
export @my_expr

macro my_expr(s::String)
    Symbol.(eachsplit(s))
end

end

```

So I can call it like this

```julia
julia> @my_expr "e1\ne2\ne3"

```

but not like this:

```julia
julia> str="e1\ne2\ne3";
julia> @my_expr str

```

Defining another method within the module `MyExpr` like this:

```julia
macro my_expr(s::Symbol)
  str = eval(s)
  Symbol.(eachsplit(s))
end

```

won’t work, because `str` is not defined within the MyExpr module.  
Can I somehow interpolate the macro’s argument at the  
scope where I call it, without resorting to `Meta.parse()`?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 15, 2025, 9:18pm UTC](https://discourse.julialang.org/t/evaluate-symbol-passed-to-macro/124810/2 "2025-01-15T21:18:04Z")

</div>

> [@Mikhail\_Kagalenko](#):
>
> because `str` is not defined within the MyExpr module.

Macros get extra arguments for where they’re called, including what module:

```julia
julia> macro pass()
         println( __module__ )
       end
@pass (macro with 1 method)

julia> @pass
Main

julia> module Blah
         using Main: @pass
         @pass
       end;
Main.Blah

```

You can use that module for `Core.eval`.

However, that will only work for global variables, there’s no way for any version of `eval` to run code in a local scope. Macros typically generate an expression instead of executing code within its call; expressions can be inserted into a local scope. It looks like you’re deliberately not doing that; is there a reason why `my_expr` isn’t just a normal function?

---

<div class="post-metadata">

**Author:** ![Mikhail\_Kagalenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikhail_kagalenko/32/13257_2.png) [@Mikhail\_Kagalenko](https://discourse.julialang.org/u/Mikhail_Kagalenko)\
**Post date:** [January 15, 2025, 9:25pm UTC](https://discourse.julialang.org/t/evaluate-symbol-passed-to-macro/124810/3 "2025-01-15T21:25:08Z")

</div>

`@my_expr` is a simplified illustration, the actual macro generates functions. It is `@problemset` from the “ProblemSet” registered module.

---

<div class="post-metadata">

**Author:** ![Mikhail\_Kagalenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikhail_kagalenko/32/13257_2.png) [@Mikhail\_Kagalenko](https://discourse.julialang.org/u/Mikhail_Kagalenko)\
**Post date:** [January 16, 2025, 7:13am UTC](https://discourse.julialang.org/t/evaluate-symbol-passed-to-macro/124810/4 "2025-01-16T07:13:28Z")

</div>

> [@Benny](#):
>
> Macros typically generate an expression instead of executing code within its call; expressions can be inserted into a local scope. I

I suppose I could construct and return to a local scope an Expr calling the macro with the string argument.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 16, 2025, 12:16pm UTC](https://discourse.julialang.org/t/evaluate-symbol-passed-to-macro/124810/5 "2025-01-16T12:16:25Z")

</div>

You would need a function, not a macro, to do that, and the returned `Expr` is not inserted into the local scope and can only be `eval`-uated in the global scope. If you only need to generate functions in the global scope, that’s fine, and not all expressions are transformed in macros e.g. `@eval` loops.

---

<div class="post-metadata">

**Author:** ![Mikhail\_Kagalenko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikhail_kagalenko/32/13257_2.png) [@Mikhail\_Kagalenko](https://discourse.julialang.org/u/Mikhail_Kagalenko)\
**Post date:** [January 16, 2025, 4:55pm UTC](https://discourse.julialang.org/t/evaluate-symbol-passed-to-macro/124810/6 "2025-01-16T16:55:25Z")

</div>

> [@Benny](#):
>
> If you only need to generate functions in the global scope, that’s fine

Practically speaking, that’s what I really need (maybe I’ll make a string macro for the purpose), but why such fundamental limitation exists? Why can’t expressions be evaluated in a local  
scope? From earlier comments by @yuyichao I seem to recall that this is a matter of some principle.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [January 16, 2025, 7:42pm UTC](https://discourse.julialang.org/t/evaluate-symbol-passed-to-macro/124810/7 "2025-01-16T19:42:51Z")

</div>

I think it has to do with the world age mechanism. If you were allowed to eval inside functions into the current world age then the compiler could not optimize your code as much or at all, as any of the functions being called could be overwritten at any time. So going into a function call Julia considers the world fixed until it returns to top level. You can get around that using `invokelatest` which comes with a performance penalty. There’s also RuntimeGeneratedFunctions.jl

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 17, 2025, 12:32am UTC](https://discourse.julialang.org/t/evaluate-symbol-passed-to-macro/124810/8 "2025-01-17T00:32:37Z")

</div>

World age is definitely one reason, but it’s also just for optimizable compilation in general. If you could generate and execute an expression during a method call and its variables are supposed to behave as if it belonged to the method’s scope, then it’s too late for the compiler to do anything about the expression and the compiler has to abandon many important optimizations for the rest of the method. So, expressions can only be incorporated into a local scope at parse-time (macros) or compile-time (`@generated` functions).

One such optimization is storing data for variables of known types in relative addresses of the stack frame; otherwise all variables would have to live in `Dict{Symbol, Any}` on the heap… Even languages with implementations that do put variables on the heap tend to make it unfeasible for users to dynamically change local scopes.
