2024 Clojure 状态调查! 中分享您的想法。

欢迎!请查看“关于”页面以了解更多信息。

0投票
编译器

此补丁更改了与宏一起使用时元数据的行为。现在,&form 元数据与宏调用 sexpr 的元数据合并。这使得用户在宏调用中既可以为内部或外部形式进行类型提示,并得到一些更好的结果。在以前,直接使用宏展开的元数据。这阻止了以下代码,而不使用反射进行工作:

(.trim ^String (when true "hello "))

补丁: 2013-10-11_CLJ-865_Fix-With-Tests.diff
审查: Timothy Baldridge

--------- 实施细节 ----------

正如在 http://groups.google.com/group/clojure/browse_thread/thread/2690cb6ca0e8beb8 中讨论的,对于表示宏的表达式进行类型提示,如 (.length ^String (doto (identity "x") prn)),存在一个“意外因素”。在这里,doto 宏丢弃了 &form 上的元数据,导致反射查找。这导致表示函数调用的表达式可以进行类型提示,而一般情况下表示宏的表达式不能。可以将 doto 宏重写为尊重其 &form 元数据,但为所有存在的宏做这件事既冗长又容易出错。相反,我建议更改编译器,以便宏展开自动保留元数据。

附加的第一个补丁添加了我提出的行为的测试:此测试失败。应用第二个补丁后,该测试通过。

在接受我的补丁之前,有几个问题需要进一步考虑:
- 我不确定是否正确地格式化了 Java 代码。我的编辑器没有正确配置为自动获取 clojure/core 风格。
- 我的解决方案是获取 &form 元数据,移除 :line/:file 键,然后将其与返回的元数据合并,其中 &form 占优。我不确定这是否是所有情况的正确方法,尽管它对 :tag 元数据来说有效。
- 我通过更改编译器实现了这一点,这使其相当重量级。如果不想更改编译器,则可以调整 defmacro,但这会涉及更多工作,并且更难测试(例如,多参数使事情复杂化)。似乎更简单地对待宏展开作为一个黑盒,然后对结果进行元数据调整,而不是修改它们的实际 defmacro 代码。
- 如果宏展开为不是 IObj 的东西,例如整数,那么我的补丁会静默地丢弃调用者的元数据。抛出一个异常会更好吗?

16 答案

0投票
by

评论者:amalloy

因此,我着手在 clojure.core/defmacro 中而不是在 clojure.lang.Compiler/macroexpand1 中执行这个更改。结果比预期的还要糟糕:我没有意识到在引导过程中我们还没有 syntax-quote 或 apply,因此编写非平凡的宏展开需要大量的 (list foo (list bar 'local-name)) 等等。

我相信我写出的版本并不理想,但似乎在 defn 上搭车并使用 alter-var-root 在其中进行元数据管理会简单一些,而不是一开始就扩展到正确的东西。

不管怎样,附件中的补丁#3可以用作替代#2,解决 clojure.core 中而不是 clojure.lang 的问题。补丁#1中添加的测试在两种情况下都通过。

0投票
by

评论者:amalloy

我意识到我可以用一个命名的私有函数而不是一个匿名函数来做这件事,减少了 defmacro 本身需要生成的杂乱程度。我认为,如果Clojure实现比Java实现更受青睐,补丁4比补丁3要好。

0投票
by

评论者:[email protected]

我更倾向于在 Java 中使用补丁0002,而不是0003或0004。补丁0002将如何调用宏函数(特别是额外的 &form 和 &env 参数)的知识保留在一个地方,即 macroexpand1,而不是将此知识在 core.clj 中重复。注意补丁0001只是测试。

提出的默认宏展开行为比我们目前所拥有的更有用,但是我还想更多地考虑以下两个细节

1) 为了获得更有用的默认行为,宏编写者失去了消费他们的 ∧form 元数据和在没有 ∧form 元数据覆盖的情况下控制结果形式元数据的能力。也就是说,宏不再对其输出形式完全控制。

2) 上述规则(1)为 :line 和 :file 设置了硬编码的异常,这时 ∧form 元数据无法覆盖宏返回的结果。

0投票
by

评论者:amalloy

此补丁包含此问题的所有先前补丁。

在clj-dev邮件列表中,Andy Fingerhut建议一个新的元数据键,以便宏作者可以指定“我已经检查了它们的 &form 元数据,这正是我想要展开的表单,请不要更改元数据。” 我已经实现了这个功能,并且我认为这解决了Chouser关于需要一种“跳出”改进默认行为的方法的担忧。

一个开放式的问题是,:explicit-meta是否是最好的键?我曾经花费了一些时间追踪一个由于忘记关键词而在测试中使用:explicit-metadata而导致的bug;也许有一些更难以混淆的东西可用。

0投票

评论者:jafingerhut

2013年8月14日核准的clj-865-updated-v2-patch.txt与Alan Malloy 2012年6月1日核准的updated.patch相同。我只是对补丁进行了更新,使得它能够在测试文件macros.clj的某些上下文行由于最近的提交而损坏之后干净地应用到最新的master上。

0投票

评论者:halgari

添加了适用于master的更新补丁,并也从宏的元数据中删除了COLUMN_KEY。

0投票

评论者:halgari

添加了包含所有修复和一些更多测试的补丁。

0投票

评论者:richhickey

由于这可能会破坏某些内容,我们只需在宏名称上获取元数据来请求此操作

(defmacro ^:keep-meta simple-macro [f arg] `(~f ~arg))

或什么的

0投票

评论者:amalloy

当然,我会整理那个补丁。但我担心的是,如果它不是默认设置,它就不会被使用,我们将处于目前相同的情境,即没有任何宏这样做正确。我预见不到有人会通过他们的库来给每个宏添加^:keep-meta。

0投票

评论者:amalloy

我已经根据Rich的要求更新了补丁,但它导致了我不敢确定的问题,即在处理引用变量或私有变量时测试回归。希望有人能够运行这些测试并找出这里缺少了什么;我的改动应该是可选的,我看不出来我哪里做错了。

0投票

评论者:jafingerhut

Alan,您于2013年12月3日上传的补丁文件clj865.patch在开头和结尾有HTML垃圾代码,即使在去除这些代码后,也无法将补丁干净地应用到今天的最新Clojure master版本上。我明白您说需要更多工作,但如果是干净的补丁,其他人尝试起来会更容易。

0投票

评论者:amalloy

对不起,Andy,感谢您的提醒。我最近使用了一些不太适合开发者的电脑,但我会尽力在今天晚上修复补丁。

0投票

评论者:amalloy

这里是修复补丁的方法。我已经验证了它可以在当前master版本上干净地应用。

0投票

评论者:amalloy

为了明确起见,该文件名为clj-865.patch。我没有意识到JIRA不会清楚地显示我随附评论一起上传的文件。

0投票

评论者:jafingerhut

截至Eastwood版本0.2.0,它包括一个新警告::unused-meta-on-macro,当给宏调用应用元数据时(除clojure.core/fn外,此宏明确使用应用到其上的元数据),将发出警告。https://github.com/jonase/eastwood#unused-meta-on-macro

...