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 来实现,而不是更改编译器(如果不愿更改编译器)。然而,我认为这将涉及更多的工作并更难测试(例如,多arity 会使问题复杂化)。将宏展开视为黑盒然后修改结果元数据似乎更合适,而不是修改它们的实际 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 可以替换 #2 用于解决 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 造成的错误;可能有一种更难以混淆的键可供选择。

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

这是一个补丁的修复。我已经验证,这个补丁可以干净地应用到当前的主版本。

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

...