# Generate in macroexpand time an expression that interpolates variables in runtime

**URL:** <https://discourse.julialang.org/t/generate-in-macroexpand-time-an-expression-that-interpolates-variables-in-runtime/8816>\
**Category:** General Usage\
**Tags:** macros, metaprogramming\
**Created:** [February 4, 2018, 6:12pm UTC](https://discourse.julialang.org/t/generate-in-macroexpand-time-an-expression-that-interpolates-variables-in-runtime/8816 "2018-02-04T18:12:45Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![dfdx](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dfdx/32/120_2.png) [@dfdx](https://discourse.julialang.org/u/dfdx)\
**Post date:** [February 4, 2018, 6:12pm UTC](https://discourse.julialang.org/t/generate-in-macroexpand-time-an-expression-that-interpolates-variables-in-runtime/8816/1 "2018-02-04T18:12:45Z")

</div>

I have a struct to represent symbolic variable:

```julia
struct Var
    name::Symbol
    value::Any 
end

```

Now I want to generate a function expression that takes objects of type `Var` and returns an expression made from their `.name` fields, e.g. like this:

```julia
function +(x::Var, y::Var)
    return Expr(:call, :+, x.name, y.name)
end 

x = Var(:a, 1) # note: julia variable `x`, but internal name is :a 
y = Var(:b, 2)

x + y # ==> :(a + b) 

```

Code generation will be wrapped into a macro, so I’ll refer to this stage - stage where we generate function like above - as a **macroexpand time**. On the contrary, the returned expression, e.g. `Expr(:call, :+, x.name, y.name)` can only be created in **runtime** since `x.name` and `y.name` aren’t known beforehand.

I have many function like this with different signatures, e.g.:

```julia
+(x::Var, y::Var)
exp(x::Var)
^(x::Var, n::Int)
...

```

Obviously, I don’t want to copy-paste function body for of these cases but instead to have something like:

```julia
@symbolic +(x::Var, y::Var)
@symbolic exp(x::Var)
@symbolic ^(x::Var, n::Int)
...

```

So the list of argument names is always different (we can ignore types for a moment). Now the basic setup of a problem looks like this:

```julia
f_vars = [:x, :y] # the list of `Var` names from function signature, known during macroexpand time
quote
    ex_vars = ... # something that returns [:a, :b] in runtime
    Expr(:call, :op, ex_vars...)
end

```

The missing piece is the code to get `ex_vars`, i.e. values of `x.name` and `y.name` during runtime. Note, that during macroexpand time we known the names of variables (i.e. `x` and `y`), but not their values.

First I tried the following:

```julia
:(ex_vars = [v.name for v in $f_vars])

```

But `f_vars` is a list of symbols, not `Var`s, so `v.name` fails with `type Symbol has no field name`.

So I tried to interpolate each `v` first, meaning that interpolation should happen in runtime:

```julia
:(ex_vars = [$(v).name for v in $f_vars])

```

Surely, it’s interpolated in macroexpand time instead. I also tried multiple options with `Expr(:$, ...)` and `QuoteNode(...)`, but couldn’t find anything working. One more attempt with `esc`in hope it passes `$` as is:

```julia
:(ex_vars = [esc($(v).name) for v in $f_vars])

```

But it also tries to interpolate `v` right in macroexpand time.

Any other options?

---

<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:** [February 5, 2018, 6:53am UTC](https://discourse.julialang.org/t/generate-in-macroexpand-time-an-expression-that-interpolates-variables-in-runtime/8816/4 "2018-02-05T06:53:12Z")

</div>

I believe this does what you’re asking for:

```julia
macro symbolic(ex::Expr)
    @assert ex.head == :call
    op = ex.args[1]
    args = ex.args[2:end]
    names = []
    types = []
    for arg in args
        @assert arg.head == :(::)
        push!(names, arg.args[1])
        push!(types, arg.args[2])
    end
    Expr(:(=),
        esc(ex),
        Expr(:call, :Expr, QuoteNode(:call), QuoteNode(op), [Expr(:., name, QuoteNode(:name)) for name in names]...)
    )
end

```

Usage:

```julia
julia> struct Var
           name::Symbol
       end

julia> x = Var(:a)
Var(:a)

julia> y = Var(:b)
Var(:b)

julia> import Base: +

julia> @symbolic +(x::Var, y::Var)
+ (generic function with 181 methods)

julia> x + y
:(a + b)

```

However, there’s a lot of clutter in that macro definition. MacroTools.jl helps clean this up a lot:

```julia
using MacroTools

macro symbolic2(ex::Expr)
    @assert @capture(ex, op_(args__))
    argdata = splitarg.(args)
    names = [a[1] for a in argdata]
    Expr(:(=),
        esc(ex),
        Expr(:call, :Expr, QuoteNode(:call), QuoteNode(op), [Expr(:., name, QuoteNode(:name)) for name in names]...)
        )
end

```

Usage:

```julia
julia> @symbolic2 -(x::Var, y::Var)
- (generic function with 1 method)

julia> x - y
:(a - b)

```

---

<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:** [February 5, 2018, 6:56am UTC](https://discourse.julialang.org/t/generate-in-macroexpand-time-an-expression-that-interpolates-variables-in-runtime/8816/5 "2018-02-05T06:56:10Z")

</div>

By the way, my general method for approaching this (and other metaprogramming problems) is to manually generate the output I want, then `dump` that expression and then write the code to generate that output. For example, in this case I did the following:

```julia
julia> dump(:(+(x::Var, y::Var) = Expr(:call, :+, x.name, y.name)))
Expr
  head: Symbol =
  args: Array{Any}((2,))
    1: Expr
      head: Symbol call
      args: Array{Any}((3,))
        1: Symbol +
        2: Expr
          head: Symbol ::
          args: Array{Any}((2,))
            1: Symbol x
            2: Symbol Var
          typ: Any
        3: Expr
          head: Symbol ::
          args: Array{Any}((2,))
            1: Symbol y
            2: Symbol Var
          typ: Any
      typ: Any
    2: Expr
      head: Symbol block
      args: Array{Any}((2,))
        1: Expr
          head: Symbol line
          args: Array{Any}((2,))
            1: Int64 1
            2: Symbol REPL[1]
          typ: Any
        2: Expr
          head: Symbol call
          args: Array{Any}((5,))
            1: Symbol Expr
            2: QuoteNode
              value: Symbol call
            3: QuoteNode
              value: Symbol +
            4: Expr
              head: Symbol .
              args: Array{Any}((2,))
                1: Symbol x
                2: QuoteNode
              typ: Any
            5: Expr
              head: Symbol .
              args: Array{Any}((2,))
                1: Symbol y
                2: QuoteNode
              typ: Any
          typ: Any
      typ: Any
  typ: Any

```

which showed me exactly where I needed to put the appropriate `QuoteNodes` and where to replace an `Expr` with an `Expr(:call, :Expr, ...)`

Edit: originally I was mistakenly generating `Expr(:+, x.name, y.name)` instead of `Expr(:call, :+, x.name, y.name)`.

---

<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:** [February 5, 2018, 6:59am UTC](https://discourse.julialang.org/t/generate-in-macroexpand-time-an-expression-that-interpolates-variables-in-runtime/8816/6 "2018-02-05T06:59:35Z")

</div>

There’s one remaining issue with my macro, which is that it will try to do `x.name` for _all_ args, regardless of their type. Presumably that’s not what you want for non-`Var` arguments, but I think instead of doing `x.name` you can just do `_getname(x)` and have `_getname(x::Var) = x.name` with `_getname(x) = x` as a fallback for all other inputs.

---

<div class="post-metadata">

**Author:** ![dfdx](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dfdx/32/120_2.png) [@dfdx](https://discourse.julialang.org/u/dfdx)\
**Post date:** [February 5, 2018, 9:45pm UTC](https://discourse.julialang.org/t/generate-in-macroexpand-time-an-expression-that-interpolates-variables-in-runtime/8816/7 "2018-02-05T21:45:47Z")

</div>

Works like a charm, thanks!

> By the way, my general method for approaching this (and other metaprogramming problems) is to manually generate the output I want, then dump that expression and then write the code to generate that output.

Yeah, I walked around this approach multiple times, but this code generation during code generation just mixed up my mind. Now I know that it works in such complicated circumstances as well 🙂
