【问题标题】:Is nesting eval calls in macros bad practice?在宏中嵌套 eval 调用是不好的做法吗?
【发布时间】:2018-12-09 11:25:56
【问题描述】:

为了说明,假设我有以下宏计算真值表中的行:

macro bool_to_lit(a)
    eval(a) ? (x -> x) : (x -> !x)
end

macro make_clause(xs, bools, res)
    lits = map((x -> @eval @bool_to_lit $x), bools.args)
    clause_elements = map.(lits, xs.args)
    and_res = all(push!(clause_elements, res))
    return and_res
end

#@make_clause((false, false), (false, false), true) returns true

@bool_to_lit 根据参数的值返回一个闭包,@make_clause 使用结果来计算自己的值。但是,由于@make_clause 使用@eval,我的理解是它实际上运行@bool_to_lit(因此不只是执行语法转换)。

在这种情况下避免使用嵌套的@eval 会更好(如更快和生成更简洁的代码),这样整个宏树的整个结果在 rutime 只评估一次?

是否在更容易编码(在使用@eval时将嵌套宏视为函数)之间进行权衡vs.正确性(避免嵌套 @eval) 时只进行编译时语法转换?

【问题讨论】:

标签: macros julia eval dsl compile-time


【解决方案1】:

(免责声明:我稍微缩短了您代码的逻辑。也许我这样做有误,但大体相同。)

在大多数情况下,不,您不应该在宏中使用eval。你给的东西有两种可能的选择。首先,如果您只需要宏处理文字布尔值(即值truefalse),那么这些值将直接存储在 AST 中,您可以在编译时直接进行正常计算:

julia> macro make_clause_literal(xs, bools, res)
           clause_elements = map((lit, arg) -> lit == arg, bools.args, xs.args)
           res && all(clause_elements)
       end
@make_clause_literal (macro with 1 method)

julia> @macroexpand @make_clause_literal((false, false), (false, false), true)
true

如果输入真的是文字布尔值,应该添加一些检查。

另一方面,如果您想很好地放入其他表达式,请将代码转换为执行相同操作的高效代码,并将评估留给运行时:

julia> macro make_clause(xs, bools, res)
           clause_elements = map((lit, arg) -> :($lit == $arg), bools.args, xs.args)
           esc(:($res && $(foldr((e,f) -> :($e && $f), clause_elements))))
       end
@make_clause (macro with 1 method)

julia> @macroexpand @make_clause((false, false), (false, false), true)
:(true && (false == false && false == false))

julia> @macroexpand @make_clause((false, false), (false, x), y)
:(y && (false == false && x == false))

在避免中间数组和短路方面,构造&& 的序列应该尽可能好。

我推荐的第三种选择是编写一个正常的运行时函数来执行子句评估,并根据调用来重写上述任何一个宏。我把它留作练习。您也可以结合使用这两种方法并尽可能在编译时评估表达式,但我猜编译器已经在某种程度上这样做了。

【讨论】:

  • 谢谢!该示例仅用于说明,我不是在寻找运行时优化,而是仅在编译时进行计算。我实际上正在尝试Type systems as macros 中描述的想法
  • 您是在编译时尝试通过 SAT 求解进行类型检查,还是该示例真的只是编造的?
  • 我实际上是该领域的真正业余爱好者(甚至不是理科毕业生),目前我只是想跟上 Julia 宏系统的速度。我的代码取自论文草稿中的第一个示例(无类型最小布尔语言 DSL)。有关实现该想法的非常棒的示例,请参阅lexi-lambda's Hackett
  • 那里的语法示例非常令人兴奋......并且旨在实现双向类型检查作为一种宏语言对于麻醉师来说是一个非常令人印象深刻的目标 - 赞!回到 Julia,您可能想阅读 this。如果您对所有内容都足够熟悉,可以再次介绍抽象,请查看MacroTools
  • 谢谢!这些都是非常有用的链接。是的,我可能不会走得太远,因为我怀疑这会非常困难,但我对计算机科学充满热情,我觉得这对像 julia 这样的科学计算语言来说是一个真正的好处
猜你喜欢
  • 1970-01-01
  • 2019-02-12
  • 2014-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多