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

欢迎!有关更多关于它是如何工作的信息,请参阅关于 页面。

+1
ClojureScript

这看起来像 ClojureScript 1.11.54 中的一个错误。

(conj {} [:a 1] '([:b 2] [:c 3]) '([:d 4]))

在 JVM 上的 Clojure 中这是一个错误

cljs.user> (conj {} [:a 1] '([:b 2] [:c 3]) '([:d 4]))
{:a 1, :b 2, :c 3, :d 4}
cljs.user> :cljs/quit
nil
user> (conj {} [:a 1] '([:b 2] [:c 3]) '([:d 4]))
Execution error (ClassCastException) at user/eval17754 (REPL:25).
class clojure.lang.PersistentVector cannot be cast to class java.util.Map$Entry (clojure.lang.PersistentVector is in unnamed module of loader 'app'; java.util.Map$Entry is in module java.base of loader 'bootstrap')
某事物 _工作_ 并不意味着它是错误。这也不意味着它是预期行为并且可以依赖。在这种情况下,这可能只是偶然发生。
在这种情况下,该工作代码在数据方面意义不大。

conj 可以将两个元素的向量连接到一个映射中,具有精确的语义。将向量列表连接到中不应工作。

换句话说 (conj {} [[:a 1] [:b 2]]) 是可以的,因为我们正在连接两个向量的向量,结果是 {[:a 1] [:b 2]}。

然而,(conj {} '([:a 1] [:b 2])) 并不应该被批准,因为它不是一个向量。

将 '([:d 4]) 仅连接到映射根本没有任何意义,而且不应该工作,但在 CLJS 中却可以工作。尤其是由于将 [[:d 4]] 连接到上不会工作。

它几乎就像 CLJS 不关心元素是一个列表还是仅仅是元素。

    (conj {} [[:a 1] [:b 2]] '([:c 3] [:d 4]))
    => {[:a 1] [:b 2], :c 3, :d 4}
某些以特定方式工作的事情不一定是有意义的。如果某件事情没有在某处进行文档记录,根据定义,它的行为是未定义的。“本不应该好”,实际上如果它没有被记录,它确实不好。并不是每个无效或未定义的行为都会抛出异常,尤其是在Clojure[Script]中。
> 如果某件事情没有在某处进行文档记录,根据定义,它的行为是未定义的。

实际上,这是相反的情况。语言规定了未定义的行为是不可预测的。这就是我们为什么会认为这是不应该依赖的未定义行为,而不是一个错误。

你说的应该是未指定行为,这通常是由于在文档或实现中忽略了一些东西造成的。在这个工单的情况下,我怀疑是后者。

我不知道你为什么尝试减轻问题。如果这是一个编译器的错误,它可能是协议实现中的问题,这可能会成为严重的问题。
> 语言规定了未定义的行为是不可预测的

如果你阅读更多与核心团队相关的讨论,未记录的内容通常被视为未定义的。至少根据我的理解和记忆,他们就是这样看待事情的。
例如,看看Sean Corfield的这条好评论: https://groups.google.com/g/clojure/c/yMHHHuK44pE/m/LeBhqdq6BQAJ
或者Alex Miller的这条明确讨论了特定内容缺乏文档的未定义行为的情况: https://groups.google.com/g/clojure/c/VwA2Un2NMxc/m/tptww3xZBwAJ
或者这条: https://groups.google.com/g/clojure/c/HtK4pqsr--8/m/KbWvq8CFCQAJ
以及许多其他例子,遍布各种媒体。

> 未指定行为

Clojure有很多是不确定的。该语言本身没有任何规范和标准,仅有一些文档(这些文档没有记录存在的一切),一些规范定义和参考实现。以前已经明确提出,确定一切都是一个目标。

例如,调用 `keys` 或 `vals` 或遍历相同的映射对象将按相同顺序返回项。但与第一个相同的映射对象可能按不同的顺序返回它们。CLJS中的映射几乎肯定会在CLJ中返回不同的顺序。

>> 如果这是一个编译器的错误,那么它可能在协议实现中,这可能会成为一个严重的问题。

如果是这种情况,那将是一个不同的讨论。

登录注册来回答这个问题。

...