MPa*_*aga 6 metaprogramming julia
我正在尝试编写一个为类型层次结构定义多种方法的宏。我试图实现的是一种任意枚举类型层次结构的order()方法,通过为类型树中的每个结构定义一个方法。
macro enum_type(type)
let type = eval(type)
next = [type]
methods = []
counter = 0
while(!isempty(next))
let current_type = pop!(next)
children = subtypes(current_type)
map(t -> push!(next, t), children)
push!(methods, :(order(::$current_type) = $(counter += 1)))
end
end
quote
$(methods...)
end
end
end
Run Code Online (Sandbox Code Playgroud)
返回的表达式似乎不在顶层计算。有没有办法返回多个顶级表达式?
所需的行为是为层次结构中的每种类型创建一个方法,例如,考虑
@macroexpand @enum_type (AbstractFloat)
Run Code Online (Sandbox Code Playgroud)
应该编写一个方法,order(...)将任意数字关联到从 AbstractFloat 开始的类型树中的每个类型。现在,带有参数 AbstractFloat 的宏的扩展是
quote
#= none:14 =#
var"#57#order"(::AbstractFloat) = begin
#= none:10 =#
1
end
var"#57#order"(::Float64) = begin
#= none:10 =#
2
end
var"#57#order"(::Float32) = begin
#= none:10 =#
3
end
var"#57#order"(::Float16) = begin
#= none:10 =#
4
end
var"#57#order"(::BigFloat) = begin
#= none:10 =#
5
end
end
Run Code Online (Sandbox Code Playgroud)
但是没有对方法声明进行评估。
看起来您的问题与生成的表达式是否为顶级无关。这与您想定义一个名为order(以及与其关联的多个方法)的通用函数这一事实相当相关,但名称order本身并未保留在宏扩展中:正如您在发布的宏扩展中所见,order已被替换为var"#57order",这是一个用户定义函数实际上不能拥有的名称。这样做是为了帮助解决宏的一个常见问题,称为卫生。
我认为您可以对宏进行的最小修改是在生成的表达式中对函数名称 ( )进行转义order,以便名称保持不变:
macro enum_type(type)
let type = eval(type)
next = [type]
methods = []
counter = 0
while(!isempty(next))
let current_type = pop!(next)
children = subtypes(current_type)
map(t -> push!(next, t), children)
# see how esc is used to prevent the method name `order` from
# being "gensymmed"
push!(methods, :($(esc(:order))(::$current_type) = $(counter += 1)))
end
end
quote
$(methods...)
end
end
end
Run Code Online (Sandbox Code Playgroud)
IIUC,这可以满足您的需求(并且方法定义仍然不是顶级的):
julia> @macroexpand @enum_type AbstractFloat
quote
#= REPL[1]:14 =#
order(::AbstractFloat) = begin
#= REPL[1]:10 =#
1
end
order(::Float64) = begin
#= REPL[1]:10 =#
2
end
order(::Float32) = begin
#= REPL[1]:10 =#
3
end
order(::Float16) = begin
#= REPL[1]:10 =#
4
end
order(::BigFloat) = begin
#= REPL[1]:10 =#
5
end
end
julia> @enum_type AbstractFloat
order (generic function with 5 methods)
julia> order(3.14)
2
Run Code Online (Sandbox Code Playgroud)
现在,在阅读您的宏时,您会想到其他一些事情:
通常不赞成在eval宏的主体内使用
let这里并不真正需要你的积木;我想把它们排除在外会更惯用
map(f, xs)生成一个包含所有值的数组[f(x) for x in xs]。如果f仅用于其副作用,foreach则应改用。(编辑:正如@CameronBieganek 所指出的,append!正是在这种特定情况下所需要的)
我认为对于这样的任务,元编程用于从(几乎)什么都没有生成顶级代码,而不是转换(用户提供的)表达式(可能在给定的范围/上下文内),我会使用@eval而不是一个宏。
所以我可能会使用如下代码:
function enum_type(type)
next = [type]
counter = 0
while(!isempty(next))
current_type = pop!(next)
append!(next, subtypes(current_type))
@eval order(::$current_type) = $(counter += 1)
end
end
Run Code Online (Sandbox Code Playgroud)
产生相同的结果:
julia> enum_type(AbstractFloat)
julia> order(3.14)
2
Run Code Online (Sandbox Code Playgroud)