Macros 如何使结构的宏生成与结构匹配的函数方法?

Macros 如何使结构的宏生成与结构匹配的函数方法?,macros,julia,Macros,Julia,请原谅标题中任何混乱的术语,但想象一下我想要一个小宏来标记我创建的结构,使其可用于某些邪恶的目的。我写了这个小模块: module Usable export @usable, isusable isusable(::Type{T}) where T = false macro usable(expr::Expr) name = expr.args[2] return quote $expr Usable.isusable

请原谅标题中任何混乱的术语,但想象一下我想要一个小宏来标记我创建的结构,使其可用于某些邪恶的目的。我写了这个小模块:

module Usable

export @usable, isusable

isusable(::Type{T}) where T = false

macro usable(expr::Expr)
    name = expr.args[2]

    return quote
        $expr
        
        Usable.isusable(::Type{$name}) = true     # This in't working
    end
end

end
但是,尝试使用我的宏

julia> include("Usable.jl")
Main.Usable

julia> using Main.Usable

julia> @usable struct Foo
           bar::String
       end
导致

ERROR: UndefVarError: Foo not defined
结构显然定义得很好

julia> Foo("soup")
Foo("soup")

因此,这个定义似乎比我预期的要早。我显然遗漏了什么,但我不知道是什么。

请始终查看宏的输出:

julia> @macroexpand @usable struct Foo
                  bar::String
              end
quote
    #= REPL[1]:11 =#
    struct Foo
        #= REPL[4]:2 =#
        bar::Main.Usable.String
    end
    #= REPL[1]:13 =#
    (Main.Usable.Usable).isusable(::Main.Usable.Type{Main.Usable.Foo}) = begin
            #= REPL[1]:13 =#
            true
        end
end
问题是宏输出在它定义的模块中被扩展,这会弄乱名称的含义。在本例中,我们希望
Foo
引用定义它的名称空间,但是:

quote
    #= REPL[1]:11 =#
    struct Foo
        #= REPL[10]:2 =#
        bar::String
    end
    #= REPL[1]:13 =#
    Usable.isusable(::Type{Foo}) = begin
            #= REPL[1]:13 =#
            true
        end
end
其实很简单,只需转义输出:

macro usable(expr::Expr)
   name = expr.args[2]

   return esc(quote
       $expr
       $Usable.isusable(::Type{$name}) = true
   end)
end
但一定要再次阅读宏文档<代码>esc非常复杂,您不希望盲目地将其应用于您编写的所有内容


另一件事(我希望做到这一点)是拼接模块本身--
$Usable
--而不是按名称引用它。否则,如果在外部重命名模块名称,则可能会出现问题。

在所描述的场景中,几乎总是应该使用Julia强大的类型系统和多分派机制,而不是宏。(也许你有很好的理由这么做,但这是给其他人的信息。) 该模式只是通过一个抽象类型来定义所需的行为,该抽象类型随后由自定义
结构继承

这里有一个示例,用于将
可比较的
行为添加到复合
结构
s

abstract type Comparable end

import Base.==
==(a::T, b::T) where T <: Comparable =
    getfield.(Ref(a),fieldnames(T)) == getfield.(Ref(b),fieldnames(T))
抽象类型比较结束
进口基地==
==(a::T,b::T)其中T struct MyStruct MyStruct([“hello”])==MyStruct([“hello”])
真的

非常有魅力!我尝试转义名称并使用QuoteNode,但我从未想过转义整个返回表达式。有些人认为默认情况下应转义宏,并使用opt-in。默认情况下,我更喜欢卫生,但正如我们在这里看到的那样,这是有道理的。对于非恶性案件来说,这很好。我们可能还想在这里提到特征,以防OP真的需要多重继承。是的,不幸的是MI在Julia中不可用。也许是茱莉亚2.0。
julia> struct MyStruct <: Comparable 
    f::Vector{String}  
end;

julia> MyStruct(["hello"]) == MyStruct(["hello"])
true