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

这是一个已知的问题。

https://clojure.atlassian.net/browse/ASYNC-91

...