# What are the rules for qualified/unqualified symbols in macros? (or is it a bug?)

**URL:** <https://discourse.julialang.org/t/what-are-the-rules-for-qualified-unqualified-symbols-in-macros-or-is-it-a-bug/59293>\
**Category:** General Usage\
**Tags:** macros\
**Created:** [April 14, 2021, 6:12pm UTC](https://discourse.julialang.org/t/what-are-the-rules-for-qualified-unqualified-symbols-in-macros-or-is-it-a-bug/59293 "2021-04-14T18:12:15Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![mobius-eng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mobius-eng/32/99_2.png) [@mobius-eng](https://discourse.julialang.org/u/mobius-eng)\
**Post date:** [April 14, 2021, 6:12pm UTC](https://discourse.julialang.org/t/what-are-the-rules-for-qualified-unqualified-symbols-in-macros-or-is-it-a-bug/59293/1 "2021-04-14T18:12:16Z")

</div>

Let’s say I want to automate making lots of very similar looking, but semantically distinct types:

```julia
macro make_struct(arg)
    quote
        struct $(esc(arg))
            x
        end
        export $(esc(arg))
    end
end

```

It creates a struct and then exports it. A quick check:

```julia
julia> @macroexpand @make_struct(xxxx)
quote
    #= REPL[1]:3 =#
    struct xxxx
        #= REPL[1]:4 =#
        x
    end
    #= REPL[1]:6 =#
    export xxxx
end

```

So far so good. I may want to process these structs somehow else. So, let’s put their names into a vector and instantiate all the structs from that list:

```julia
const structs_to_make = [:Foo, :Bar]

macro make_many_structs()
    quote
        $([:(@make_struct $(st)) for st in structs_to_make]...)
    end
end

```

But here `$(st)` get inserted in the expansion of `@make_struct` in two different ways depending where it appears:

```julia
julia> @macroexpand @make_many_structs
quote
    #= REPL[3]:3 =#
    begin
        #= REPL[1]:3 =#
        struct Main.Foo
            #= REPL[1]:4 =#
            x
        end
        #= REPL[1]:6 =#
        export Foo
    end
    begin
        #= REPL[1]:3 =#
        struct Main.Bar
            #= REPL[1]:4 =#
            x
        end
        #= REPL[1]:6 =#
        export Bar
    end
end

```

And this will an error in `struct` definition. If I escape `st` in `@make_many_structs`:

```julia
macro make_many_structs()
    quote
        $([:(@make_struct $(esc(st))) for st in structs_to_make]...)
    end
end

```

it doesn’t expand correctly in `export` statement:

```julia
julia> @macroexpand @make_many_structs
quote
    #= REPL[9]:3 =#
    begin
        #= REPL[1]:3 =#
        struct Foo
            #= REPL[1]:4 =#
            x
        end
        #= REPL[1]:6 =#
        export $(Expr(:escape, :Foo))
    end
    begin
        #= REPL[1]:3 =#
        struct Bar
            #= REPL[1]:4 =#
            x
        end
        #= REPL[1]:6 =#
        export $(Expr(:escape, :Bar))
    end
end

```

Why is it that in one place a symbol gets a qualified name and in another — unqualified? Or is it a bug? Also, any way to workaround?

---

<div class="post-metadata">

**Author:** ![chris-b1](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chris-b1/32/14165_2.png) [@chris-b1](https://discourse.julialang.org/u/chris-b1)\
**Post date:** [April 14, 2021, 6:29pm UTC](https://discourse.julialang.org/t/what-are-the-rules-for-qualified-unqualified-symbols-in-macros-or-is-it-a-bug/59293/2 "2021-04-14T18:29:43Z")

</div>

This may or may not be a bug, I’m not sure - but as a workaround, you can just `esc` the entire expression returned by your macro and it seems to work.

If you don’t need macro hygiene anywhere, you don’t need to do targeted esc’s like that.

```julia
 macro make_struct(arg)
    esc(quote
            struct $arg
                x
            end
            export $arg
        end
    )
end

julia> @macroexpand @make_many_structs
quote
    #= REPL[9]:3 =#
    begin
        #= REPL[6]:3 =#
        struct Foo
            #= REPL[6]:4 =#
            x
        end
        #= REPL[6]:6 =#
        export Foo
    end
    begin
        #= REPL[6]:3 =#
        struct Bar
            #= REPL[6]:4 =#
            x
        end
        #= REPL[6]:6 =#
        export Bar
    end
end

```

---

<div class="post-metadata">

**Author:** ![mobius-eng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mobius-eng/32/99_2.png) [@mobius-eng](https://discourse.julialang.org/u/mobius-eng)\
**Post date:** [April 15, 2021, 7:02am UTC](https://discourse.julialang.org/t/what-are-the-rules-for-qualified-unqualified-symbols-in-macros-or-is-it-a-bug/59293/3 "2021-04-15T07:02:18Z")

</div>

Thanks for the tip!

I guess, I am going to one an issue and see what higher powers say 🙂

**EDIT**  
Link to the issue: [https://github.com/JuliaLang/julia/issues/40490](https://github.com/JuliaLang/julia/issues/40490)

---

<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:** [April 15, 2021, 10:57pm UTC](https://discourse.julialang.org/t/what-are-the-rules-for-qualified-unqualified-symbols-in-macros-or-is-it-a-bug/59293/4 "2021-04-15T22:57:57Z")

</div>

This is not a direct answer, but I wanted to try to offer a non-macro solution. And that’s because, while I quite enjoy that Julia has metaprogramming facilities, there do seem to tricky edge cases whenever doing any kind of macro composition.

I would consider making a `NamedTuple` that has the fields of the struct, and write wrapper types, each with one field containing the `NamedTuple` values. I think this pattern is a good way to split up naming/semantics from structure, which then allows you to use the structure without resorting to metaprogramming.

I wrote for my own purposes (and with a different motivation) [GitHub - goretkin/NamedNamedTuples.jl](https://github.com/goretkin/NamedNamedTuples.jl) , and I suggest you look first at [https://github.com/BeastyBlacksmith/ProtoStructs.jl](https://github.com/BeastyBlacksmith/ProtoStructs.jl) if this pattern sounds like it would be useful to you.

* * *

That assumes that you don’t have any other reason to need `structs_to_make` (or even if you do, perhaps you can live with some repetition), and if I’m understanding correctly that you want to minimize repeating the whole definition of a `struct`, likely with more fields than just `x`.

Note you won’t have the same expressiveness in constraining the types of the field, but I have often found those constraints to be pesky. e.g., whenever I want a `Rational` of two different types, say `Int64` and a type from [`StaticNumbers.jl`](https://github.com/perrutquist/StaticNumbers.jl), thereby getting a less feature-full version of [`FixedPointNumbers.jl](https://github.com/JuliaMath/FixedPointNumbers.jl) for a lot less code. Those kinds of constraints (and many more) could arguably be _better_ handled by something like [“Argument standardization”](https://timholy.github.io/SnoopCompile.jl/stable/pgdsgui/#Argument-standardization) (how delightful it is to have a name and explanations for this concept).

And, if that particular kind of expressiveness is important, and you are okay with choosing a type name instead of using a `NamedTuple` (which I like thinking of as an anonymous `struct` with no identity other than its own structure), then you could use a `struct` instead of a `NamedTuple`. Personally, I think the `NamedTuple` is the right choice because I try (with questionable benefit) to avoid choosing names when it isn’t too inconvenient. Even the field name of the single field inside the wrapper types, I would name `_`. It is only legal when there’s a single field and so it seems least arbitrary name.

---

<div class="post-metadata">

**Author:** ![mobius-eng](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mobius-eng/32/99_2.png) [@mobius-eng](https://discourse.julialang.org/u/mobius-eng)\
**Post date:** [April 16, 2021, 7:21am UTC](https://discourse.julialang.org/t/what-are-the-rules-for-qualified-unqualified-symbols-in-macros-or-is-it-a-bug/59293/5 "2021-04-16T07:21:58Z")

</div>

I am failing to follow how this will help…

---

<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:** [April 16, 2021, 2:20pm UTC](https://discourse.julialang.org/t/what-are-the-rules-for-qualified-unqualified-symbols-in-macros-or-is-it-a-bug/59293/6 "2021-04-16T14:20:46Z")

</div>

You want to avoid this kind of duplication

```julia
struct Foo1{A, B, C, D}
    a::A
    b::B
    c::C
    d::D
end

struct Foo2{A, B, C, D}
    a::A
    b::B
    c::C
    d::D
end

struct Foo3{A, B, C, D}
    a::A
    b::B
    c::C
    d::D
end
# [...]

```

right?

What I’m suggesting is basically

```julia
# common_structure{A,B,C,D} = NamedTuple{(:a, :b, :c, :d), Tuple{A, B, C, D}}
common_structure = NamedTuple{(:a, :b, :c, :d)}

struct Foo1{T <: common_structure}
    _::T
end

struct Foo2{T <: common_structure}
    _::T
end

struct Foo3{T <: common_structure}
    _::T
end

```

That alone gives

```julia
julia> foo1 = Foo1(common_structure((1,2,3,4)))
Foo1{NamedTuple{(:a, :b, :c, :d), NTuple{4, Int64}}}((a = 1, b = 2, c = 3, d = 4))

julia> foo1 = Foo1((a=1, b=2, c=3, d=4)) # equivalent
Foo1{NamedTuple{(:a, :b, :c, :d), NTuple{4, Int64}}}((a = 1, b = 2, c = 3, d = 4))

julia> foo1.a
ERROR: type Foo1 has no field a
Stacktrace:
 [1] getproperty(x::Foo1{NamedTuple{(:a, :b, :c, :d), NTuple{4, Int64}}}, f::Symbol)
   @ Base ./Base.jl:33
 [2] top-level scope
   @ REPL[23]:1

julia> foo1._.a
1

```

If you want to access the fields directly, instead of via `_`, then you can define `getproperty` appropriately. You might also want a constructor like `Foo1(a,b,c,d)`. The packages I mentioned should help with that. They do, of course, use metaprogramming for those definitions. But I like that the structure is specified as a `NamedTuple`, instead of in an expression.
