请在 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,但这需要大量的工作并且更难测试(例如,多个 arity 会使事情变得复杂)。似乎更好将宏扩展视为一个黑盒,然后对结果进行元数据调整,而不是修改它们的实际 defmacro 代码。
- 如果宏扩展成不是一个 IObj,例如一个 Integer,则我的补丁会静默地丢弃调用者的元数据。是否抛出异常会更好?

16 条回答

0 投票
by

评论者:amalloy

因此我进行了修改,在clojure.core/defmacro中而不是在clojure.lang.Compiler/macroexpand1中进行这个更改。这种情况甚至比我预期的还要糟糕:我没有意识到在引导阶段我们还没有语法引用或应用,因此编写一个非平凡的宏展开需要大量的(list foo (list bar 'local-name))等等。

我相信我编写的版本可能不是最优的,但似乎是利用defn更简单,然后使用alter-var-root来模拟元数据管理,而不是一开始就扩展到正确的内容。

无论如何,附带的补丁#3可以直接应用于clojure.core而不是clojure.lang以解决问题。补丁#1中添加的测试不论哪种情况都通过。

0 投票
by

评论者:amalloy

我意识到我可以用一个命名的私有函数而不是匿名函数来完成这个任务,从而减少defmacro本身需要生成的混乱。如果Clojure实现比Java实现更受欢迎,我认为补丁4严格优于补丁3。

0 投票
by

评论者:[email protected]

我更倾向于在Java中对patch 0002进行修改而不是0003或0004。Patch 0002在macroexpand1中维护了如何调用宏函数(特别是额外的&form和&env参数)的知识,而不是在core.clj中也复制这一知识。注意补丁0001仅仅是测试。

建议的默认宏展开行为比我们目前拥有的更有用,但有两个细节我想多考虑一下。

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

2) 如上所述的规则(1)对:line和:file具有硬编码的异常,在这种情况下,&form元数据无法覆盖宏返回的结果。

0 投票
by

评论者:amalloy

此补丁包含了这个问题的所有以前的补丁。

在clj-dev邮件列表上,Andy Fingerhut提出了一个新的元数据键,允许宏的作者指定“我已经查看他们的 form 元数据,这个表单正是我想要展开的,请不要再更改元数据。”我已经实现了这个功能,并认为这解决了Chouser关于需要一种方式“跳出”改进默认行为的担忧。

一个悬而未决的问题是,:explicit-meta 是否是使用正确的键?我花了一些时间追踪到一个错误,是因为我忘记使用关键字,在测试中使用 :explicit-metadata;也许有更难以混淆的东西可用。

0 投票
by

评论者:jafingerhut

2013年8月14日的clj-865-updated-v2-patch.txt与Alan Malloy的更新.patch(2012年6月1日)相同。我只是更新了补丁,以便在测试文件 macros.clj 中的一些上下文行因最近提交而变得错误后,能够干净地应用于最新的master。

0 投票
by

评论者:halgari

添加了针对master的更新补丁,并从宏的元数据中删除了COLUMN_KEY。

0 投票
by

评论者:halgari

添加补丁,包含所有修复内容以及一些额外的测试。

0 投票
by

评论者:richhickey

鉴于这可能会破坏某些东西,我们只需在宏名称上获取元数据以请求此操作

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

或类似的东西

0 投票
by

评论者:amalloy

当然,我会合并这个补丁。不过,我真的担心,如果这不是默认的,它可能永远不会被使用,我们将会处于与现在相同的情况,即没有任何宏这样做是正确的。我不认为任何人会通过他们的库为每个宏添加 ^:keep-meta。

0 投票

评论者:amalloy

我已将补丁更新为Rich要求的行为,但它导致了一个我无法理解的测试回归,即在处理引用或私有变量时。希望其他人能运行测试并找出这里缺少什么;我的更改应该是可选的,我看不出我错在哪里。

0 投票

评论者:jafingerhut

Alan,您2013年12月3日提交的clj865.patch补丁在开头和结尾处有一些HTML垃圾内容,即使移除了这些内容,它也无法与今天最新的Clojure master版本干净地应用。我明白你说它还需要更多的工作,但如果它能干净地应用,对想要尝试的人来说会更容易。

0 投票

评论者:amalloy

抱歉Andy,谢谢你指出这一点。我最近没有接触过非常友好的开发者电脑,但我今晚会尝试修复补丁。

0 投票

评论者:amalloy

这是一个修复补丁。我验证了一下,这个补丁可以干净地应用到当前主版本上。

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

...