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