# Interpolation in macro calls

**URL:** <https://discourse.julialang.org/t/interpolation-in-macro-calls/25530>\
**Category:** General Usage\
**Tags:** question\
**Created:** [June 21, 2019, 3:59pm UTC](https://discourse.julialang.org/t/interpolation-in-macro-calls/25530 "2019-06-21T15:59:58Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![cscherrer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cscherrer/32/7631_2.png) [@cscherrer](https://discourse.julialang.org/u/cscherrer)\
**Post date:** [June 21, 2019, 3:59pm UTC](https://discourse.julialang.org/t/interpolation-in-macro-calls/25530/1 "2019-06-21T15:59:58Z")

</div>

Now and again, I see macros that are able to interpolate their arguments. Here’s an example with BenchmarkTools:

```julia
julia> using BenchmarkTools

julia> ex = :(sin(3))
:(sin(3))

julia> f(x) = @btime cos($x)
f (generic function with 1 method)

julia> f(ex)
  3.186 ns (0 allocations: 0 bytes)
0.9900590857598653

```

Note that the `cos($x)` is not evaluated globally, but rather within the function scope.

There’s a huge potential here, but I haven’t been able to get my head around it (I’m still using `@eval` and `invokelatest`). The source code for `@btime` seems maybe a bit much to start with. Is there a general design pattern here I’m missing?

I’d really appreciate a very simple example that achieves this effect, or any help on the right way to approach metaprogramming that has reasonable composability.

---

<div class="post-metadata">

**Author:** ![mauro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mauro3/32/292_2.png) [@mauro3](https://discourse.julialang.org/u/mauro3)\
**Post date:** [June 21, 2019, 8:21pm UTC](https://discourse.julialang.org/t/interpolation-in-macro-calls/25530/2 "2019-06-21T20:21:45Z")

</div>

I think what happens is that `@btime` just does something clever with the expression generated by interpolation syntax. Consider:

```julia
julia> macro m(e)
       @show e
       1
       end
@m (macro with 1 method)

julia> @m sin($x)
e = :(sin($(Expr(:$, :x))))
1

```

So `@btime` then presumably does some tricks to make the `x` not a slow global. Maybe such as doing a `const xyz = x` where `xyz` would be a gensym. But I’m just guessing.

> [@cscherrer](#):
>
> help on the right way to approach metaprogramming that has reasonable composability.

I don’t see the connection here.

---

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [June 21, 2019, 10:47pm UTC](https://discourse.julialang.org/t/interpolation-in-macro-calls/25530/3 "2019-06-21T22:47:32Z")

</div>

> [@mauro3](#):
>
> So `@btime` then presumably does some tricks to make the `x` not a slow global. Maybe such as doing a `const xyz = x` where `xyz` would be a gensym. But I’m just guessing.

Yes, that is more or less what happens. Below is an expansion of `@btime cos($(x+1))`, with comments explaining the parameters of `generate_benchmark`:

```julia
julia> using BenchmarkTools
julia> using MacroTools
julia> (@macroexpand @btime cos($(x+1))) |> rmlines |> MacroTools.alias_gensyms
quote
    local manatee = begin
      (BenchmarkTools).generate_benchmark_definition(
         Main, # eval_module
         Symbol[], # out_vars
         Symbol[Symbol("cheetah")], # setup_vars
         $(Expr(:copyast, :($(QuoteNode(:(cos(cheetah))))))), (Core._expr)(:block, $(Expr(:copyast, :($(QuoteNode(nothing))))), # code
         (Core._expr)(:(=), Symbol("cheetah"), x + 1)), # setup
         $(Expr(:copyast, :($(QuoteNode(nothing))))), # teardown
         (BenchmarkTools.Parameters)() # params
      )                                                                                                                                                                        
    end
    (BenchmarkTools).warmup(manatee)
    (BenchmarkTools).tune!(manatee)
    local (sanddollar, bison) = (BenchmarkTools).run_result(manatee)
    local crow = (BenchmarkTools).minimum(sanddollar)
    local donkey = (BenchmarkTools).allocs(crow)
    println(" ", (BenchmarkTools).prettytime((BenchmarkTools).time(crow)), " (", donkey, " allocation", if donkey == 1
            ""
        else
            "s"
        end, ": ", (BenchmarkTools).prettymemory((BenchmarkTools).memory(crow)), ")")
    bison
end

```

In particular, you see that the “code” has been transformed into basically `cos(cheetah)`, with a “setup” which says: `cheetah = x+1`.

  

You’ll probably be able to find the part of the `BenchmarkTools` which does this if you search for it. In the [`GFlops.jl`](https://github.com/triscale-innov/GFlops.jl) package, I try to implement the same kind of feature (separation between code and preliminary setup) [here](https://github.com/triscale-innov/GFlops.jl/blob/master/src/count_ops.jl#L12), in order to use it [there](https://github.com/triscale-innov/GFlops.jl/blob/master/src/count_ops.jl#L20).

Hopefully you’ll find this example useful.

---

<div class="post-metadata">

**Author:** ![cscherrer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cscherrer/32/7631_2.png) [@cscherrer](https://discourse.julialang.org/u/cscherrer)\
**Post date:** [June 22, 2019, 2:45pm UTC](https://discourse.julialang.org/t/interpolation-in-macro-calls/25530/4 "2019-06-22T14:45:02Z")

</div>

> [@mauro3](#):
>
> I don’t see the connection here.

If you have a function that calls another function, you can inline the latter without changing the behavior of the former. This composability makes functions easy to reason about.

Scoping is also a difficulty. With functions, the rules are… not necessarily easier, but maybe just more familiar to most of us. There’s also lots of documentation about scoping for functions.

With macros, all of this goes out the window. Your `@m` macro is a good example of this: it works fine at top-level, but try calling it from a function and…

```julia
julia> g(x) = @m sin($x)
e = :(sin($(Expr(:$, :x))))
g (generic function with 1 method)

julia> g(ex)
1

```

From what I’ve learned about macros to this point, I’m guessing the solution is “you need to escape it”. But exactly _where_ the `esc` should go is still (for me) a matter of trial and error.

There’s also a big challenge in figuring out _which things are allowed_. As a simple example, I had thought “macro definitions must return an `Expr`” was a hard constraint, but your `@m` returns an Int. It’s also surprising to me that `@show` doesn’t allow interpolation, but `@m` does. What is it about `@show` and `@m` that lead to the difference in interpolation ability?

For a long time, I had hoped there was a way to build “`@eval` but with local scope”. Then I could use a function to build an `Expr`, and finally call a macro to compile the code. I’ve been told this is impossible, but `@show` and `@btime` seem to do a sort of local evaluation. There’s also a design pattern I see in [MLStyle.jl](https://github.com/thautwarm/MLStyle.jl) of using functions to build an expression, and wrapping the whole thing in a macro. So I know you can do this, I just don’t see how to learn the rules of the game.

I’d really, _really_ love a “Metaprogramming in Julia” book, assuming an understanding of functions and carefully building up an approach to thinking about macros, with strong emphasis on getting the mental model right. Currently I don’t really see a path to building a deep understanding of this stuff.

> [@ffevotte](#):
>
> In the [`GFlops.jl`](https://github.com/triscale-innov/GFlops.jl) package, I try to implement the same kind of feature (separation between code and preliminary setup) [here](https://github.com/triscale-innov/GFlops.jl/blob/master/src/count_ops.jl#L12), in order to use it [there](https://github.com/triscale-innov/GFlops.jl/blob/master/src/count_ops.jl#L20).

Thanks for the link, I’ll check it out. Always great to have more examples of this, just need to work through what they all have in common. And `@macroexpand` is always great advice 🙂

---

<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:** [June 22, 2019, 3:49pm UTC](https://discourse.julialang.org/t/interpolation-in-macro-calls/25530/5 "2019-06-22T15:49:05Z")

</div>

I think there may be some misunderstandings here. I’ll write out how I understand this process and maybe that can help. Macros are functions that take in code (structured as an `Expr`) and return new code.

So, when you write

```julia
g(x) = @m sin($x)

```

and press enter at the REPL, the julia parser will ready the text, see the `@m` sigil and say "okay, I need to apply the macro `m` to the expression `Expr(:call, [:sin, Expr(:$, [:x])])`. This process does _not_ wait for `g` to be called on an argument. It happens immediately. The macro `m` only operates once (unless you redefine `g`) and so that single time it operates, it prints out the expression it received `:(sin($(Expr(:$, :x))))` and then it returns `1`. This means that after macroexpansion, the function body of `g` gets transformed into

```julia
g(x) = 1

```

Macros do not know about runtime values, they only know about syntax trees. When a macro receives an expression with a `$x` in it, it can’t interpolate the value of `x` into the syntax tree because it reads the syntax tree before `x` ever has a value! So the interpolation syntax in macros is not given any actual meaning in julia.

Instead, when a macro is given an expression with `$` in it, it assumes you’re going to give your own meaning to `$x`. In the case of BenchmarkTools.jl they return code that has to wait until runtime to receive the value of `x` and then splice that value into an expression which is evaluated and benchmarked. Nowhere in the actual body of the macro do they have access to the value of `x` though.

---

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [June 22, 2019, 5:23pm UTC](https://discourse.julialang.org/t/interpolation-in-macro-calls/25530/6 "2019-06-22T17:23:39Z")

</div>

In addition to @Mason’s detailed and useful answer, I would like to add that macros are often shortly described as “mapping syntax to syntax”. I find this relatively useful to understand what you can and can’t do with a macro:

- the input syntax should:
  - be parseable (i.e. Julia’s parser must be able to read the macro arguments and build a correct syntax tree out of it)
  - lead to a syntax tree which is understood by your macro
  - but nothing prevents you from assigning a meaning to the syntax that is completely different from what Julia does

- the output syntax should:
  - be evaluable by Julia (i.e. it must obey the standard Julia rules that give meaning to an expression)

In particular:

- `1` is something that can be evaluated by Julia, so it is a legitimate macro output
- `$` has a standard meaning in Julia; it interpolates a value in strings, quoted expressions, etc. But nothing prevents you from assigning it a different meaning in the input syntax of your macro. And that is what `@btime` (from `BenchmarkTools`) and `@count_ops` (from `GFlops`) do, but this is only because something specific is performed in these macros to handle `$`. Most other macros don’t have any code handling `$` specifically, and simply forward `$`-based expressions in their input syntax to the same `$`-based expressions in their output syntax (which might or might not be valid code in Julia, and thus will or will not cause errors when evaluating).
- all this is mostly _not_ related to macro hygiene and escaping. But I would advise you to try and understand how macros work (i.e. get a mental model of the evaluation process, with the parsing, macro expansion, and evaluation stages). In a second stage, you can start understanding the details about hygiene and escaping. Of course it’s hard to dissociate the two in practice, so you might have to blindly escape everything from your first macros (knowing that it’s a bad thing and trying not to get into the habit of doing it). When you get something that works, you can start escaping only what is needed.

---

<div class="post-metadata">

**Author:** ![cscherrer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cscherrer/32/7631_2.png) [@cscherrer](https://discourse.julialang.org/u/cscherrer)\
**Post date:** [June 23, 2019, 2:30pm UTC](https://discourse.julialang.org/t/interpolation-in-macro-calls/25530/7 "2019-06-23T14:30:16Z")

</div>

> [@Mason](#):
>
> Macros do not know about runtime values, they only know about syntax trees. When a macro receives an expression with a `$x` in it, it can’t interpolate the value of `x` into the syntax tree because it reads the syntax tree before `x` ever has a value!

Ok, this is really helpful. I know macros take inputs as an `Expr`. But it had seemed `$` was a magical exception to this rule. Good to know that’s not the case.

> [@ffevotte](#):
>
> I find this relatively useful to understand what you can and can’t do with a macro

Thank you, this seems like a good way of thinking about things. But what’s still not clear is the right way of implementing a “call by reference”-like macro pattern.

For example, I have a function `sourceRand` to take a `Model` and build an `Expr` that evaluates to a function that samples from it. Here’s my current implementation:

```julia
function makeRand(m :: Model)
    fpre = @eval $(sourceRand(m))
    f(;kwargs...) = Base.invokelatest(fpre; kwargs...)
end

rand(m::Model; kwargs...) = makeRand(m)(;kwargs...)

```

I use this approach all over the place. I _know_ there are better ways of doing this, but any macro approach I’ve tried either can’t dereference `m`, or evaluates to an unevaluated `Expr`.

---

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [June 23, 2019, 2:56pm UTC](https://discourse.julialang.org/t/interpolation-in-macro-calls/25530/8 "2019-06-23T14:56:17Z")

</div>

Could you send a contrived but complete example, including a tentative implementation of sourcerand and an example of Model?

With this info we might be able to propose a solution.

---

<div class="post-metadata">

**Author:** ![cscherrer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cscherrer/32/7631_2.png) [@cscherrer](https://discourse.julialang.org/u/cscherrer)\
**Post date:** [June 23, 2019, 3:16pm UTC](https://discourse.julialang.org/t/interpolation-in-macro-calls/25530/9 "2019-06-23T15:16:58Z")

</div>

> [@ffevotte](#):
>
> Could you send a contrived but complete example, including a tentative implementation of sourcerand and an example of Model?

Sure! A `Model` looks like this:

```julia
struct Model
    args :: Vector{Symbol}
    body :: Vector{Statement}
end

```

I have a `@model` macro that builds one of these from an expression. Here’s a simple example:

```julia
julia> normalModel
@model x begin
        μ ~ Normal(0, 5)
        σ ~ HalfCauchy(3)
        x ~ Normal(μ, σ) |> iid(10)
    end

julia> normalModel.args
1-element Array{Symbol,1}:
 :x

julia> normalModel.body
6-element Array{Soss.Statement,1}:
 Soss.LineNumber(:(#= /home/chad/git/jl/Soss/src/examples.jl:31 =#))
 Soss.Follows(:μ, :(Normal(0, 5)))                                  
 Soss.LineNumber(:(#= /home/chad/git/jl/Soss/src/examples.jl:32 =#))
 Soss.Follows(:σ, :(HalfCauchy(3)))                                 
 Soss.LineNumber(:(#= /home/chad/git/jl/Soss/src/examples.jl:33 =#))
 Soss.Follows(:x, :(Normal(μ, σ) |> iid(10)))                       

```

Then here’s `sourceRand`:

```julia
function sourceRand(m::Model)
    m = canonical(m)
    proc(m, st::Let) = :($(st.x) = $(st.rhs))
    proc(m, st::Follows) = :($(st.x) = rand($(st.rhs)))
    proc(m, st::Return) = :(return $(st.rhs))
    proc(m, st::LineNumber) = nothing

    body = buildSource(m, proc) |> striplines
    
    argsExpr = Expr(:tuple,freeVariables(m)...)

    stochExpr = begin
        vals = map(stochastic(m)) do x Expr(:(=), x,x) end
        Expr(:tuple, vals...)
    end
    
    @gensym rand
    
    flatten(@q (
        function $rand(args...;kwargs...) 
            @unpack $argsExpr = kwargs
            $body
            $stochExpr
        end
    ))

end

```

`buildSource` is a little helper function for this:

```julia
function buildSource(m, proc; kwargs...)
    q = @q begin end
    for st in m.body
        ex = proc(m, st; kwargs...)
        isnothing(ex) || push!(q.args, ex)
    end
    q
end

```

Oh, and seeing the result might help:

```julia
julia> sourceRand(normalModel)
:(function ##rand#368(args...; kwargs...)
      @unpack () = kwargs
      μ = rand(Normal(0, 5))
      σ = rand(HalfCauchy(3))
      x = rand(iid(10, Normal(μ, σ)))
      (μ = μ, σ = σ, x = x)
  end)

```

---

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [June 23, 2019, 8:59pm UTC](https://discourse.julialang.org/t/interpolation-in-macro-calls/25530/10 "2019-06-23T20:59:08Z")

</div>

Thanks, I think now I start understanding your real problem.

I think the easiest solution would be to build the specific `rand` function at the same time when the model is declared (using `@model`). Would it be possible?

---

<div class="post-metadata">

**Author:** ![cscherrer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cscherrer/32/7631_2.png) [@cscherrer](https://discourse.julialang.org/u/cscherrer)\
**Post date:** [June 23, 2019, 10:22pm UTC](https://discourse.julialang.org/t/interpolation-in-macro-calls/25530/11 "2019-06-23T22:22:49Z")

</div>

> [@ffevotte](#):
>
> I think the easiest solution would be to build the specific `rand` function at the same time when the model is declared (using `@model` ). Would it be possible?

No, and this has been the whole problem.

The point of the library is to provide a convenient way to describe a `Model`, and then to allow code to be generated for various kinds of inference. One goal is for models to be first-class. We should be able to write a model in terms of Distributions, or in terms of other models, composing arbitrarily before finally choosing an inference algorithm and generating code for it.
