请在2024年Clojure状态调查!中分享您的观点。

欢迎!请查阅关于页面以获取更多关于它是如何工作的信息。

+2投票
ClojureScript

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

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 投票
...