Spurious parentheses in generated expression

I have a regression test that’s failing. It’s testing a helper function to a macro that adds a source slot to a struct definition and modifies the constructor methods appropriately.

That helper function returns this expression

Base.@__doc__(struct Sequence <: BNFNode
            source::(Union){Nothing, LineNumberNode}
            elements::Tuple{Vararg{<:BNFNode}}
            function Sequence(elements...)
                new(nothing, elements)
            end
            function Sequence(source::Union{Nothing, LineNumberNode}, elements...)
                new(source, elements)
            end
        end) == 

The text expects it to return

Base.@__doc__(struct Sequence <: BNFNode
            source::Union{Nothing, LineNumberNode}
            elements::Tuple{Vararg{<:BNFNode}}
            function Sequence(elements...)
                new(nothing, elements)
            end
            function Sequence(source::Union{Nothing, LineNumberNode}, elements...)
                new(source, elements)
            end
        end)

The only difference I see is the Parentheses around Union in the source slot. I’d think those parentheses are meanaingless though and I don’t know why they’re there.

Note that the results of parsing the slot definition both with and without those parentheses are identical:

Meta.@dump(source::(Union){Nothing, LineNumberNode})
Expr
  head: Symbol ::
  args: Array{Any}((2,))
    1: Symbol source
    2: Expr
      head: Symbol curly
      args: Array{Any}((3,))
        1: Symbol Union
        2: Symbol Nothing
        3: Symbol LineNumberNode

Meta.@dump(source::Union{Nothing, LineNumberNode})
Expr
  head: Symbol ::
  args: Array{Any}((2,))
    1: Symbol source
    2: Expr
      head: Symbol curly
      args: Array{Any}((3,))
        1: Symbol Union
        2: Symbol Nothing
        3: Symbol LineNumberNode

One of my frustrations with Julia is the number of different syntaxes and the non-obvious (to me) correspondences between them.

What can I do to my test to make it not sensitive to this peculiar and pointless difference?

The test is at AnotherParser.jl/test_note_BNFNode_location.jl at a5b3496b0f7f45f3a65479b3dc4f99c2e964c790 · MarkNahabedian/AnotherParser.jl · GitHub

I believe the difference is this:

julia> a = :(Union{Foo,Bar})
:(Union{Foo, Bar})

julia> b = :($Union{Foo,Bar})
:((Union){Foo, Bar})

julia> a.args[1]
:Union

julia> b.args[1]
Union

i.e. one of your expressions interpolates $Union into the expression, whereas the other one just writes (the symbol) Union directly.

This means they are indeed different expressions: you could evaluate a in a module where Union = Dict is defined and you’d get a Dict{Foo,Bar}, whereas evaluating b will always give Union{Foo,Bar}.

The parentheses are intentional, to distinguish code from interpolated values (which look like code).

Wow. Thanks for your quick response.

I don’t think that’sit though as the slot definition expression is constant:

        push!(constructors2,
              :(function $(bindings2[:fname])(source::Union{Nothing, LineNumberNode},
                                              $(bindings2[:fargs]...))
                    $(map(bindings2[:fbody]) do e
                          postwalk(e) do e
                              if iscall(e, :new)
                                  Expr(:call, :new, :source, e.args[2:end]...)
                              else
                                  e
                              end
                          end
                      end...)
                end))

Dooo. That wasn’t the right bit of code.

The problem was here

Expr(:curly, :Union, Nothing, LineNumberNode)),

where Union wasn’t quoted.

Thanks.