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

欢迎!请查看 关于 页面以了解更多关于此功能的信息。

+2
ClojureScript

前几天我遇到了一个非常奇怪的错误。

clojurescript 中的 and 宏具有一个优化,它将自身展开成这样(显然,当它可以确保其参数返回布尔值时——否则,它将扩展为使用 if

(js* "~{} && ~{}" <arg1> <arg2>)

然而,在 core.async 的 go 块中,这会导致问题,因为代码生成器似乎将两个参数从 js* 形式中提升出来,在宏展开的代码中产生如下内容

(let [arg1 <arg1> arg2 <arg2>] (js* "~{} && ~{}" arg1 arg2))

这导致在 go 块中对 and 的参数进行有时是急切地计算(而不是按照预期那样懒惰计算)。

例如,考虑这段代码,该代码检查一个值是否是序列,并且如果是,检查它的第一个元素是否是符号 foo

(go (let [x 5] (and (seq? x) (= (first x) 'foo))))

这失败了,因为 (seq? x)(= (first x) 'foo) 都被编译器检测到返回布尔值,导致上述行为(扩展为 js* 形式而不是 if)。

在这种情况下,这会抛出一个异常,因为对 (first x) 的调用在数字上执行(当 (first x) 应该不被评估,因为 (seq? x) 为假时)。

2 回答

+1

这个问题已存在很长时间,并且并不仅限于 core.async,任何对宏体进行深度分析的第三方宏都可能受到影响。特定于 core.async 的问题是
- https://clojure.atlassian.net/browse/ASYNC-91
- https://clojure.atlassian.net/browse/ASYNC-158

发生的情况是
- go 宏需要展开其体以执行其分析。
- 宏展开将 and/or 转换为包装 JavaScript 等效布尔操作符的 js* 特殊形式
- go 宏不知道如何处理 js* 形式,因此它假定应用顺序,因为在大多数情况下这是正确的做法

解决这个问题主要有两种方法
- 修改 ClojureScript 中 and/or 的实现,使其适用于第三方宏分析以更不透明的形式宏展开
- 要求第三方宏解析 js* 特殊形式的模板,以检测 JavaScript 布尔操作符等非应用顺序形式

Clojure 不是一个指定语言,维护者可以决定走哪条路。根据 ClojureScript 团队的说法,当前 and/or 的实现没有问题。另一方面,仅仅为了能够写出可以远程写第三方宏,就依赖于一个解析建立在 JavaScript 之上的临时未指定模板语言的解析器,这有点过分了。我已经多次在 Slack 上尝试解释这个问题,得到的答案从“我不明白你在说什么”到“它将被列为低优先级”。到目前为止,我对在未来可预见的时间内解决这个问题表示怀疑。

0 投票
...