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

欢迎!有关如何使用此功能,请参阅关于页面以获取更多信息。

0
编译器

此补丁更改了在宏调用中使用元数据时的行为。元数据 &form 现在与宏调用 sexpr 的元数据合并。这使用户能够在宏调用中为内部或外部形式进行类型提示并获得较好的结果。以前,直接使用宏展开的元数据。这阻止了如下代码在没有反射的情况下工作

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

补丁: 2013-10-11_CLJ-865_Fix-With-Tests.diff
screened by: 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

评论者:amalloy

因此,我决定在 clojure.core/defmacro 而不是 clojure.lang.Compiler/macroexpand1 中做这个更改的工作。结果甚至比我预期的更糟:我没有意识到我们在引导阶段还没有语法引号或 apply,因此编写任何非平凡的宏展开都需要大量的 (list foo (list bar 'local-name)) 等等。

我相信我编写的版本不是最佳的,但似乎比一开始就展开到正确的东西要简单得多,可以通过依赖 defn,然后使用 alter-var-root 来模拟元数据管理来实现。

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

0

评论者:amalloy

我发现,我可以用一个命名私有函数而不是匿名函数来做这件事,这样可以减少 defmacro 自身需要生成的杂乱。我认为,如果Clojure实现优先于Java实现,补丁 4 优于补丁 3。

0

评论者:[email protected]

我更喜欢在 Java 中的补丁 0002,而不是 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在我测试中导致的bug;可能有一些更不容易混淆的键可供选择。

0

评论者:jafingerhut

2013年8月14日更新的clj-865-updated-v2-patch.txt与2012年6月1日更新的Alan Malloy的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

...