Query regarding namespaces when adding a method using a macro

I am confused about how namespaces are being applied in the following code, and would appreciate some help.

module A

using LinearAlgebra

abstract type AbsType end

struct SubType <: AbsType end

macro newmethod()
	quote
		function A.Diagonal(S::A.SubType) end
	end
end

@newmethod

function Diagonal(S::AbsType) end # this should be a different function

end

What I expect: the Diagonal defined in the module A should be different from the one in LinearAlgebra, as the function was neither explicitly imported nor was the method defined by specifying the namespace.

What I find:

julia> methods(A.Diagonal)
# 9 methods for type constructor:
 [1] LinearAlgebra.Diagonal(S::Main.A.SubType)
     @ Main.A REPL[1]:11
 [2] LinearAlgebra.Diagonal(A::LinearAlgebra.Bidiagonal)
     @ ~/.julia/juliaup/julia-1.10.4+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/special.jl:14
 [3] LinearAlgebra.Diagonal(A::LinearAlgebra.SymTridiagonal)
     @ ~/.julia/juliaup/julia-1.10.4+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/special.jl:23
 [4] LinearAlgebra.Diagonal(s::LinearAlgebra.UniformScaling, m::Integer)
     @ ~/.julia/juliaup/julia-1.10.4+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/special.jl:271
 [5] LinearAlgebra.Diagonal(D::LinearAlgebra.Diagonal)
     @ ~/.julia/juliaup/julia-1.10.4+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/diagonal.jl:105
 [6] LinearAlgebra.Diagonal(v::AbstractVector{T}) where T
     @ ~/.julia/juliaup/julia-1.10.4+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/diagonal.jl:14
 [7] LinearAlgebra.Diagonal(A::LinearAlgebra.Tridiagonal)
     @ ~/.julia/juliaup/julia-1.10.4+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/special.jl:34
 [8] LinearAlgebra.Diagonal(A::AbstractMatrix)
     @ ~/.julia/juliaup/julia-1.10.4+0.x64.linux.gnu/share/julia/stdlib/v1.10/LinearAlgebra/src/diagonal.jl:98
 [9] LinearAlgebra.Diagonal(S::Main.A.AbsType)
     @ Main.A REPL[1]:17

Why is there a method LinearAlgebra.Diagonal(S::Main.A.AbsType)? I didn’t ask for this.

This only happens if the method definition Diagonal(S::AbsType) comes after the macro call @newmethod, and this seems to add a new method to LinearAlgebra.Diagonal instead of to A.Diagonal.

I’m not sure what actually happens here, but it seems the module qualification A. in the macro somehow serves to override the explicit import requirement. Perhaps because LinearAlgebra.Diagonal is available in the A module, and the definition is qualified with a module name?

Anyway, depending on what you want to achieve, you should perhaps escape the function name:

macro newmethod()
	quote
		function esc(Diagonal)(S::A.SubType) end
	end
end

or the entire quote:

macro newmethod()
	quote
		function Diagonal(S::A.SubType) end
	end |> esc
end

to avoid macro hygiene renaming it to something like #1#Diagonal.

1 Like

I’m actually trying to debug a scenario where this worked accidentally when it shouldn’t have, so I’m not really looking for a workaround. In my use case, the last function should have been defined as

function LinearAlgebra.Diagonal(S::AbsType) end

but, perplexingly, this worked without the qualification, which seems like a bug?

I do want all the Diagonals to refer to the LinearAlgebra one, especially the one inside the macro. Moreover, the macro is intended to be used downstream, and I don’t want to assume that LinearAlgebra is loaded at the macro call site. So, ideally, I want to use A.Diagonal to refer to LinearAlgebra.Diagonal within the macro, which is guaranteed to be defined.

The first suggestion above doesn’t appear to work:

julia> module A

       using LinearAlgebra

       abstract type AbsType end

       struct SubType <: AbsType end

       macro newmethod()
               quote
                       function esc(Diagonal)(S::A.SubType) end
               end
       end

       @newmethod

       function Diagonal(S::AbsType) end

       end
WARNING: replacing module A.
ERROR: syntax: invalid function name "#90#esc(A.Diagonal)" around REPL[1]:11
Stacktrace:
 [1] top-level scope
   @ REPL[1]:15

Perhaps I’m misunderstanding how this is meant to be used.

The second solution does answer the original question, as it correctly defines Diagonal within A.

It would still be good to understand what’s happening in the original case, though.

I agree that the second method definition works because of an apparent bug, or an undocumented feature.

Anyway, you should just define the macro as:

macro newmethod()                                                                            
    quote                                                                                    
        function LinearAlgebra.Diagonal(S::A.SubType) end                                    
    end                                                                                      
end                                                                                          

It will expand to something that works, and it’s clearer what’s intended.

julia> @macroexpand A.@newmethod
quote
    #= REPL[13]:9 =#
    function (Main.A.LinearAlgebra).Diagonal(var"#26#S"::(Main.A.A).SubType)
        #= REPL[13]:9 =#
        #= REPL[13]:9 =#
    end
end
1 Like

The behavior above seems to be a bug, where methods may be added to a constructor without it being imported: