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 元数据,但对现有所有宏进行此操作既乏味又容易出错。相反,我提议修改编译器,使宏展开自动保留元数据。

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

在我接受我的补丁之前,有几个问题需要进一步考虑
- 我不确定我实际上 whether Java 代码格式正确。我的编辑器并没有很好地自动配置 clojure/core 风格。
- 我的解决方案是获取 &form 元数据,删除 :line/:file 键,然后与返回的元数据合并,其中 &form 优先。我不确定这是否是所有情况下都正确的方法,尽管它对于 :tag 元数据有效。
- 通过修改编译器实现了这一目标,这使得它相当重。相反,可以调整 defmacro,但如果不希望修改编译器,这可能需要更多的工作并且更难以测试(例如,多参数会使事情复杂化)。看起来,将宏展开视为一个黑盒子,然后对结果进行元数据调整,而不是修改实际的 defmacro 代码,似乎是更好的方法。
- 如果宏展开成某种不是 IObj 的东西,比如一个 Integer,那么我的补丁默默地丢弃了调用者的元数据。抛出异常会更好吗?

16 答案

0 投票

评论者:amalloy

因此我决定在clojure.core/defmacro而不是clojure.lang.Compiler/macroexpand1中做出这些更改。这比我想的要糟糕得多:我没有意识到在我们当前的引导阶段还没有syntax-quote或apply,因此编写非平凡的宏扩展需要大量(list foo (list bar 'local-name))等等。

我相信我写的版本并不最优,但与直接扩展到正确的东西相比,我认为在defn上搭建并使用alter-var-root来模拟元数据管理要简单一些。

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

0 投票

评论者:amalloy

我意识到我可以用一个命名的私有函数而不是匿名函数来做这件事,这减少了defmacro本身需要生成的混乱。我认为,如果Clojure实现是首选,那么补丁4在严谨性上优于补丁3。

0 投票

评论者:[email protected]

我更喜欢补丁0002在Java中,而不是0003或0004。补丁0002将如何调用宏函数的知识(特别是额外的&form和&env参数)放在了一个地方,即macroexpand1,而不是在core.clj中也重复这个知识。注意补丁0001仅仅是测试。

拟议的默认宏扩展行为比我们目前拥有的更有用,但我还想稍微深入思考两个细节。

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

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

0 投票

评论者:amalloy

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

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

一个未解决的问题是我们是否应该使用:explicit-meta作为正确的键。我花费了一些时间追踪一个因忘记关键字而使用:explicit-metadata的测试导致的错误;可能有一个更难混淆的选择。

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,它明确使用其上的元数据)。[如何在Eastwood中使用](https://github.com/jonase/eastwood#unused-meta-on-macro)。

...