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,比如 Integer,那么我的补丁会静默地丢弃调用者的元数据。抛出一个异常会更好吗?

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.lang 中解决 clojure.core 的问题。补丁 #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 而导致的错误;可能有些更难混淆的选项可以使用。

0
by

评论者:jafingerhut

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

0
by

评论者:halgari

加入了最新的patch,该patch与master兼容,并也从宏的元数据中移除了COLUMN_KEY。

0
by

评论者:halgari

附加了一个包含所有修复和几个更多测试的补丁。

0
by

评论者:richhickey

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

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

或者类似的

0
by

评论由:amalloy发表

当然,我会准备那个补丁。但我担心,如果它不是默认的,那就根本不会用到,我们就会处于现在这种没有宏做对的情况,我现在预见不到任何人会去他们的库中给每个宏添加 ^:keep-meta。

0
by

评论由: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这个例外,clojure.core/fn会明确使用应用给它的元数据。https://github.com/jonase/eastwood#unused-meta-on-macro

...