# Macro for partially quoting (or partially evaluating) an expression

**URL:** <https://discourse.julialang.org/t/macro-for-partially-quoting-or-partially-evaluating-an-expression/53519>\
**Category:** General Usage\
**Tags:** metaprogramming\
**Created:** [January 18, 2021, 6:25am UTC](https://discourse.julialang.org/t/macro-for-partially-quoting-or-partially-evaluating-an-expression/53519 "2021-01-18T06:25:51Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![goretkin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goretkin/32/167_2.png) [@goretkin](https://discourse.julialang.org/u/goretkin)\
**Post date:** [January 18, 2021, 6:25am UTC](https://discourse.julialang.org/t/macro-for-partially-quoting-or-partially-evaluating-an-expression/53519/1 "2021-01-18T06:25:51Z")

</div>

I would like to quote expressions, but evaluate some `Symbol`s (which ones? see below). It should be possible to accomplish this using macros. I’ve tried to give an idea below of what I’m after:

````julia
"""
e.g.

```julia
julia> eval(uneval(Expr(:my_call, :arg1, :arg2)))
:($(Expr(:my_call, :arg1, :arg2)))

julia> eval(eval(uneval(:(sqrt(9)))))
3.0
```
"""
function uneval(x::Expr)
    # the `Expr` below is assumed to be available in the scope and to be `Base.Expr`
    :(Expr($(uneval(x.head)), $(map(uneval, x.args)...)))
end

# tangential TODO: determine which one
uneval(x) = Meta.quot(x)
# uneval(x) = Meta.QuoteNode(x)

"""
e.g.
julia> dump(let x = 9
       @xquote sqrt(x)
       end)
Expr
    head: Symbol call
    args: Array{Any}((2,))
        1: sqrt (function of type typeof(sqrt))
        2: Int64 9
"""
macro xquote(ex)
    uneval(ex) # TODO escape `::Symbol` in `args`.
end

````

The current behavior is

```julia
julia> dump(let x = 9
       @xquote sqrt(x)
       end)
Expr
  head: Symbol call
  args: Array{Any}((2,))
    1: Symbol sqrt
    2: Symbol x

```

but I would like the output to be what I wrote in the docstring for `@xquote`. For this _particular_ expression, the following definition produces the desired output:

```julia
macro xquote_sqrt9(ex)
    uneval(Expr(:call, sqrt, 9))
endI

```

as does explicitly quoting:

```julia
julia> dump(let x = 9
           :($(sqrt)($x))
       end)
Expr
  head: Symbol call
  args: Array{Any}((2,))
    1: sqrt (function of type typeof(sqrt))
    2: Int64 9

```

I’ve played around with `f(x) = esc(x)`, and `f(x) = Expr(:$, x)` in  
`escape_all_symbols(ex) = MacroTools.postwalk(x -> x isa Symbol ? f(x) : x, ex)`, but I’m a bit lost about how to move forward.

I think this is tangential to my question, but I won’t be evaluating every single `Symbol`. Specifically, I would not evaluate `Symbol`s that correspond to function arguments. For example, currently:

```julia
julia> dump(let x = 9
       @xquote y -> x + y
       end)
Expr
  head: Symbol ->
  args: Array{Any}((2,))
    1: Symbol y
    2: Expr
      head: Symbol block
      args: Array{Any}((2,))
        1: LineNumberNode
          line: Int64 2
          file: Symbol REPL[125]
        2: Expr
          head: Symbol call
          args: Array{Any}((3,))
            1: Symbol +
            2: Symbol x
            3: Symbol y

```

And in my desired output, `Symbol +` and `Symbol x` would be evaluated, but `Symbol y` would not be. I think I’ve got that aspect covered, however, and it would be enough for my purposes to see a definition of `@xquote` where, for example every Symbol except for `:y` is evaluated.

---

<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:** [January 18, 2021, 5:40pm UTC](https://discourse.julialang.org/t/macro-for-partially-quoting-or-partially-evaluating-an-expression/53519/2 "2021-01-18T17:40:01Z")

</div>

This is tightly related to this problem I encountered: [Zulip](https://julialang.zulipchat.com/#narrow/stream/225542-helpdesk/topic/running.20name.20resolution.20within.20a.20macro)

You essentially want to do name-resolution within a macro, but you want to go a step further than me. Consider

```julia
x = 1
let sqrt = x -> x + 1, x = 2
  @xquote sqrt(x)
end

```

There is simply no way for the macro `@xquote` to know at macroexpansion time that `:sqrt` here is not `Base.sqrt` and `x` is not `Main.x`.

However, you _could_ potentially figure out something at runtime. I think what you need

```julia
@xquote sqrt(x)

```

to give something like

```julia
quote
  let sqrt = @isdefined(sqrt) ? sqrt : QuoteNode(:sqrt), x = @isdefined(x) ? x : QuoteNode(:x)
    Expr(:call, sqrt, x)
  end |> simplify
end

```

and then have the `simplify` function be something that can recognize that `Expr(:call, sqrt, x)` can be replaced with `sqrt(x)`.

---

<div class="post-metadata">

**Author:** ![goretkin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goretkin/32/167_2.png) [@goretkin](https://discourse.julialang.org/u/goretkin)\
**Post date:** [January 18, 2021, 9:38pm UTC](https://discourse.julialang.org/t/macro-for-partially-quoting-or-partially-evaluating-an-expression/53519/3 "2021-01-18T21:38:53Z")

</div>

I will check out the related problem, thanks for the pointer and the reply.

> [@Mason](#):
>
> There is simply no way for the macro `@xquote` to know at macroexpansion time that `:sqrt` here is not `Base.sqrt` and `x` is not `Main.x` .

If I am understanding correctly, I don’t think this is what I’m asking for.  
whether you have

```julia
julia> @show sqrt;
sqrt = sqrt

```

or

```julia
julia> let sqrt=3
       @show sqrt
       end;
sqrt = 3

```

the code emitted from the macro is the same, and I’d like it to be the same for @xquote too.

I see now that I specified poorly what I want in my original post. I should show the output of `@macroexpand`, instead of showing what the macro evaluates to in each context. Thanks for bringing it to my attention.

```julia
julia> @macroexpand @xquote_sqrt9 sqrt(9)
:(Main.Expr(:call, sqrt, 9))

```

what `sqrt` resolves to is to be determined at runtime, but the expression that the macro emits just still has the `sqrt` escaped.

---

<div class="post-metadata">

**Author:** ![goretkin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goretkin/32/167_2.png) [@goretkin](https://discourse.julialang.org/u/goretkin)\
**Post date:** [January 19, 2021, 12:04am UTC](https://discourse.julialang.org/t/macro-for-partially-quoting-or-partially-evaluating-an-expression/53519/4 "2021-01-19T00:04:24Z")

</div>

> [@goretkin](#):
>
> ```julia
> julia> @macroexpand @xquote_sqrt9 sqrt(9)
> :(Main.Expr(:call, sqrt, 9))
> 
> ```

I just noticed this isn’t particularly helpful in clarifying, since two different expressions print the same way:

```julia
julia> :(Expr(:call, sqrt, 9))
:(Expr(:call, sqrt, 9))

julia> :(Expr(:call, $sqrt, 9))
:(Expr(:call, sqrt, 9))

julia> dump(:(Expr(:call, sqrt, 9)))
Expr
  head: Symbol call
  args: Array{Any}((4,))
    1: Symbol Expr
    2: QuoteNode
      value: Symbol call
    3: Symbol sqrt
    4: Int64 9

julia> dump(:(Expr(:call, $sqrt, 9)))
Expr
  head: Symbol call
  args: Array{Any}((4,))
    1: Symbol Expr
    2: QuoteNode
      value: Symbol call
    3: sqrt (function of type typeof(sqrt))
    4: Int64 9

```

I desire the `:(Expr(:call, $sqrt, 9))` one.

---

<div class="post-metadata">

**Author:** ![simeonschaub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simeonschaub/32/216566_2.png) [@simeonschaub](https://discourse.julialang.org/u/simeonschaub)\
**Post date:** [January 19, 2021, 12:35am UTC](https://discourse.julialang.org/t/macro-for-partially-quoting-or-partially-evaluating-an-expression/53519/5 "2021-01-19T00:35:48Z")

</div>

Does replacing `:sqrt` with `GlobalRef(Base, :sqrt)` do what you want? In the general case, what you are asking for would only be possible by calling `eval` inside the macro, which will miss locally defined functions and the use of `eval` during macro expansion might have other undesired consequences.

---

<div class="post-metadata">

**Author:** ![goretkin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goretkin/32/167_2.png) [@goretkin](https://discourse.julialang.org/u/goretkin)\
**Post date:** [January 19, 2021, 1:35am UTC](https://discourse.julialang.org/t/macro-for-partially-quoting-or-partially-evaluating-an-expression/53519/6 "2021-01-19T01:35:08Z")

</div>

I made a mistake in the original post. That special case macro should not have been

```julia
macro xquote_sqrt9(ex)
    uneval(Expr(:call, sqrt, 9))
end

```

but instead

```julia
macro xquote_sqrt_x(ex)
    uneval(Expr(:call, esc(:sqrt), 9))
end

```

Sorry about that, and thanks for the answers so far to the question I asked, instead of the question I meant to ask. That explains why @Mason thought I wanted to do name resolution at macro-expansion time.

I believe I have answered my own question:

````julia
using MacroTools

"""
e.g.

```julia
julia> eval(uneval(Expr(:my_call, :arg1, :arg2)))
:($(Expr(:my_call, :arg1, :arg2)))

julia> eval(eval(uneval(:(sqrt(9)))))
3.0
```

Note the special case for `:(esc(x))`.
"""
function uneval(x::Expr)
    x.head === :escape && return x
    # the `Expr` below is assumed to be available in the scope and to be `Base.Expr`
    :(Expr($(uneval(x.head)), $(map(uneval, x.args)...)))
end

# tangential TODO: determine which one
uneval(x) = Meta.quot(x)
# uneval(x) = Meta.QuoteNode(x)

escape_all_symbols(ex) = MacroTools.postwalk(x -> x isa Symbol ? esc(x) : x, ex)

"""
e.g.
julia> dump(let x = 9
       @xquote sqrt(x)
       end)
Expr
    head: Symbol call
    args: Array{Any}((2,))
        1: sqrt (function of type typeof(sqrt))
        2: Int64 9
"""
macro xquote(ex)
    uneval(escape_all_symbols(ex))
end

````

which gives the following behavior:

```julia
julia> dump(let x = 9
           @xquote sqrt(x)
       end)
Expr
  head: Symbol call
  args: Array{Any}((2,))
    1: sqrt (function of type typeof(sqrt))
    2: Int64 9

julia> dump(let x = 9, sqrt=sin
           @xquote sqrt(x)
       end)
Expr
  head: Symbol call
  args: Array{Any}((2,))
    1: sin (function of type typeof(sin))
    2: Int64 9

```
