# Problem about \`quote\` in macro definition

**URL:** https://discourse.julialang.org/t/problem-about-quote-in-macro-definition/37806
**Category:** General Usage
**Tags:** question, macros, metaprogramming
**Created:** [April 18, 2020, 2:00pm UTC](https://discourse.julialang.org/t/problem-about-quote-in-macro-definition/37806 "2020-04-18T14:00:45Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![KDr2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kdr2/32/2542_2.png) [@KDr2](https://discourse.julialang.org/u/KDr2)
#### Post date: [April 18, 2020, 2:00pm UTC](https://discourse.julialang.org/t/problem-about-quote-in-macro-definition/37806/1 "2020-04-18T14:00:45Z")

</div>

I wrote the code below:

```julia

module T

function f1 end

macro m1(sym)
    :(f1(::typeof($(esc(sym)))) = 1)
end

macro m2(sym)
    quote
        f1(::typeof($(esc(sym)))) = 1
    end
end

macro m3(sym)
    quote
        $(esc(f1))(::typeof($(esc(sym)))) = 1
    end
end

end

function test end

@show @macroexpand T.@m1(test)
@show "-" ^ 12
@show @macroexpand T.@m2(test)
@show "-" ^ 12
@show @macroexpand T.@m3(test)

```

It turns out that:

1. `m1` expands as `Main.T.f1(::Main.T.typeof(test)) = ...`
2. `m2` expands as `var"#5#f1"(::Main.T.typeof(test)) = ...`
3. `m3` expands as `(Main.T.f1)(::Main.T.typeof(test)) = ...`

My problems:

- Why `m2` doesn’t resolve `f1` correctly as `m1` does?
- Why `Main.T.f1` is put between parenthesizes in the `m3` expansion? T think `f(a...) =` and `(f)(a...) =` do NOT have the same meaning, and, how to remove the parenthesizes?

Thanks.

---

<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: [April 18, 2020, 7:13pm UTC](https://discourse.julialang.org/t/problem-about-quote-in-macro-definition/37806/2 "2020-04-18T19:13:31Z")

</div>

There are many things going on here. I’m not sure I can explain everything, but let’s at least try.

The relevant part of the documentation is the section about [macro hygiene](https://docs.julialang.org/en/v1/manual/metaprogramming/#Hygiene-1). We see there that a symbol appearing in a macro is renamed (`gensym`ed, like `var"#5#f1"` in your example) if the corresponding variable is considered _local_.

In macro `m1`, the name `f1` is considered to refer to a global variable. But in macro `m2`, it is detected as being local and is renamed. My guess is that this change in behavior comes from the implicit `begin...end` block generated by `quote...end`. But I’m actually not sure whether this is intended or not.

The reason why macro `m3` does not work is a bit tricky to explain. It is not really because of the presence of parentheses: as you can easily verify, parentheses surrounding the function name are allowed.

```julia
julia> (f)(x) = 2x
f (generic function with 1 method)

julia> f(4)
8

```

Rather, the presence of parentheses is a subtle indicator that the generated code is not really what you think it is. Let’s inspect it further:

```julia
julia> using MacroTools
julia> dump(MacroTools.striplines(@macroexpand T.@m3 foo))
Expr
  head: Symbol block
  args: Array{Any}((1,))
    1: Expr
      head: Symbol =
      args: Array{Any}((2,))
        1: Expr
          head: Symbol call
          args: Array{Any}((2,))
            1: f1 (function of type typeof(Main.T.f1)) # The function itself; not its name
            2: Expr
              head: Symbol ::
              args: Array{Any}((1,))
                1: Expr
        2: Expr
          head: Symbol block
          args: Array{Any}((1,))
            1: Int64 1

```

> **(For comparison, the same output for \`@m1\`, which works)**
>
> ```julia
> julia> dump(MacroTools.striplines(@macroexpand T.@m1 foo))
> Expr
> head: Symbol =
> args: Array{Any}((2,))
> 1: Expr
> head: Symbol call
> args: Array{Any}((2,))
> 1: GlobalRef # (qualified) name Main.T.f1
> mod: Module Main.T
> name: Symbol f1
> 2: Expr
> head: Symbol ::
> args: Array{Any}((1,))
> 1: Expr
> head: Symbol call
> args: Array{Any}((2,))
> 1: GlobalRef
> 2: Symbol foo
> 2: Expr
> head: Symbol block
> args: Array{Any}((1,))
> 1: Int64 1
> 
> ```

What we see is that, instead of the name of `f1`, we got the function itself. Let’s try and understand what happens with this whole `$(esc(...))` syntax. In the case of `$(esc(sym))`, which you got right:

1. `sym` is evaluated in the context of the macro expansion, yielding the symbol `:foo`, because that’s what the macro argument was named
2. this symbol `:foo` is escaped, meaning that it is marked to not be changed for hygiene purposes → it therefore reaches the macro expansion without modification
3. when the macro expansion gets evaluated (in the context of the macro call), `:foo` therefore refers to the function named `foo` in the current scope

Let’s follow the same steps in the case of `$(esc(f1))`:

1. `f1` is evaluated in the context of the macro expansion, yielding the _function_ `T.f1` (not its name: the function itself)

2. escaping the function doesn’t mean much; nothing is done about it regarding hygiene _( **EDIT:** to be clear, and as noted by @mcabbott, `esc` has no effect whatsoever here, and can be removed without changing anything)_

3. when the macro expansion gets evaluated, Julia tries to interpret function `T.f1` as a function name, which does not make sense =\> error

  

* * *
  

All in all, if you surround your code in a `quote...end` block (which I would advise to do), you’ll have to make it so that Julia understands `f1` as being a global variable. There are (at least) two ways of doing this:

- mark it with the `global` keyword (-\> `@m2` in the example below)
- qualify the function name with the module name (-\> `@m3` in the example below)

```julia
module T

function f1 end

macro m1(sym)
    :(f1(::typeof($(esc(sym)))) = 1)
end

macro m2(sym)
    quote
        global f1
        f1(::typeof($(esc(sym)))) = 1
    end
end

macro m3(sym)
    quote
        T.f1(::typeof($(esc(sym)))) = 1
    end
end

end

```

We can check that all three macros work:

```julia
julia> using .T: @m1, @m2, @m3, f1

julia> foo1() = 1;
julia> @m1(foo1)
f1 (generic function with 1 method)

julia> foo2() = 1;
julia> @m2(foo2)
f1 (generic function with 2 methods)

julia> foo3() = 1;
julia> @m3(foo3)

julia> methods(T.f1)
# 3 methods for generic function "f1":
[1] f1(::typeof(foo3)) in Main at REPL[1]:18
[2] f1(::typeof(foo2)) in Main at REPL[1]:12
[3] f1(::typeof(foo1)) in Main at REPL[1]:6

```

---

<div class="post-metadata">

### Author: ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)
#### Post date: [April 18, 2020, 8:34pm UTC](https://discourse.julialang.org/t/problem-about-quote-in-macro-definition/37806/3 "2020-04-18T20:34:32Z")

</div>

Thanks, that’s a nice writeup. I still find `f1` a bit surprising, but indeed `:( )` differs from `quote` by a `begin` block, which is what seems to matter. Two more cases:

```julia
macro m4(sym) # like original m3, not a function definition
    quote
        $f1(::typeof($(esc(sym)))) = 1
    end
end

macro m5(sym) # :(begin end) is like quote, local f1
    :(begin
        f1(::typeof($(esc(sym)))) = 1
    end)
end

```

```julia
julia> @m4(foo4)
ERROR: syntax: invalid function name "typeof(T.f1)()" around REPL[1]:24
julia> @m5(foo5)
#22#f1 (generic function with 1 method)

julia> methods(T.f1) # nothing for foo5
# 3 methods for generic function "f1":
[1] f1(::typeof(foo3)) in Main at REPL[1]:18
[2] f1(::typeof(foo2)) in Main at REPL[1]:12
[3] f1(::typeof(foo1)) in Main at REPL[1]:6

```

---

<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: [April 18, 2020, 8:43pm UTC](https://discourse.julialang.org/t/problem-about-quote-in-macro-definition/37806/4 "2020-04-18T20:43:22Z")

</div>

> [@mcabbott](#):
>
> I still find `f1` a bit surprising

So do I.

I really wonder whether this is a bug, but in any case I wouldn’t rely on this corner case to avoid problems here…

  

> [@mcabbott](#):
>
> Two more cases:

Thanks: I think these help clarify the explanation. I edited my answer above to be more explicit about the behavior you exhibit in `m4`: that `esc` has absolutely no effect here, and can be removed without changing anything to the issue (which is rather related to `$`).

---

<div class="post-metadata">

### Author: ![KDr2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kdr2/32/2542_2.png) [@KDr2](https://discourse.julialang.org/u/KDr2)
#### Post date: [April 19, 2020, 1:31am UTC](https://discourse.julialang.org/t/problem-about-quote-in-macro-definition/37806/5 "2020-04-19T01:31:17Z")

</div>

> [@ffevotte](#):
>
> if you surround your code in a `quote...end` block (which I would advise to do), you’ll have to make it so that Julia understands `f1` as being a global variable.

I tried to add an expression that reads `f1` to make the macro recognize `f1` as a non-local var, like this:

```julia
macro m4(sym)
    quote
        print(f1)
        f1(::typeof($(esc(sym)))) = 1
    end
end

```

But it doesn’t work, `global f1` works,thanks.

And “qualify the function name with the module name” like this

```julia
macro m3(sym)
    quote
        T.f1(::typeof($(esc(sym)))) = 1
    end
end

```

resolves `f1` as `(Main.T.T).f1` which is a little weird but works.
