请在 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 中执行了这个更改的工作。结果比我预期的还要糟糕:我没有意识到在引导过程中我们还没有语法引号或应用语法,因此编写非平凡的宏展开需要大量的 (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]

与 0003 或 0004 相比,我更喜欢 Java 中的补丁 0002。补丁 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
by

评论者为:jafingerhut

clj-865-updated-v2-patch.txt 于 2013 年 8 月 14 日与 Alan Malloy 的 updated.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
已回答 by

评论作者:amalloy

我根据 Rich 的要求更新了补丁,但它导致了我无法解决的测试回归,在处理 refer 或 private 变量时。希望有人能运行测试并找出这里缺失的东西;我的更改应该是 opt-in 的,我看不出我犯了什么错误。

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

...