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

评论者: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]

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

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

1) 为了得到更有用的默认值,宏编写者将失去消费其 &form 元数据并控制输出形式的元数据而无需通过 &form 元数据来覆盖的能力。也就是说,宏不再完全控制其输出形式。

2) 上述规则(1)有两个硬编码的例外::line 和 :file,在这里无法通过宏覆盖由宏返回的结果。

0

评论者:amalloy

此补丁合并了与此问题相关的所有先前补丁。

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

一个问题,:explicit-meta 是否是正确使用的关键?我花了一些时间追踪一个由于忘记关键字并在我测试中使用 :explicit-metadata 而导致的错误;可能有一个更不容易混淆的选项。

0

评论者:jafingerhut

于2013年8月14日更新的clj-865-updated-v2-patch.txt与Alan Malloy的更新.patch于2012年6月1日相同。我只是更新了补丁,以便在测试文件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的要求更新了补丁,但它导致了我在处理 refer 或 private 变量时的测试回归,我无法找出原因。希望其他人能够运行测试并找出这里的缺失内容;我的更改应该是可选的,我看不到我哪里犯了错。

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)

...