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

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

+1
ClojureScript

这看起来像是ClojureScript 1.11.54的bug。

(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')
某种东西能工作并不意味着它是一个bug。这也不意味着它是预期行为,并且可以被信赖。在这种情况下,这可能只是一个巧合。
在这种情况下,能够工作的代码在数据层面上几乎没有意义。

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

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

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

将 just '([:d 4]) 连接到映射上在逻辑上毫无意义,不应该工作,但在CLJS中却可以工作。尤其是在 conjing [[:d 4]] 不工作的情况下。

这几乎就像是CLJS不在乎它是元素的列表还是仅仅是一系列元素。

    (conj {} [[:a 1] [:b 2]] '([:c 3] [:d 4]))
    => {[:a 1] [:b 2], :c 3, :d 4}
某种方式在特定情况下有效并不意味着它一定合理。如果某事没有被文档记录,那么按照定义,它的行为是未定义的。"不应该很好"——确实如此,如果它的使用没有经过文档化。并非所有的无效或不定义的行为都会抛出异常,尤其是Clojure[Script]中。
> 如果某事没有在某个地方进行文档记录,那就属于未定义行为。

实际上情况正好相反。语言规定,未定义行为的可预测性是不遵守规定的。这就是我们如何知道,它是一个未定义的行为,不应该被依赖,但它不是错误。

我不知道你为什么要试图减轻这个问题。如果这是一个编译器的错误,它可能在协议实现中,这可能会成为一个严重的问题。

> 未定义行为被规定为不可预测的。
如果你阅读更多与核心团队的讨论,未文档化的通常被视为未定义。至少按照我的理解和记忆来看,他们就是这个看法。

例如,请看Sean Corfield的这篇良好评论:[link]
或者Alex Miller的这篇评论,明确谈到了在缺乏特定内容文档的情况下关于未定义行为的问题:[link]
以及这篇:[link]
还有很多其他的内容。
许多媒体都有类似的内容。

> 未指定行为

Clojure未指定的地方有很多。该语言本身没有任何规范或标准,只有文档(而不是记录存在的一切),一些spec定义和引用实现。之前已经明确表示,指定一切并不是目标。

以一个示例,调用 `keys` 或 `vals` 或迭代相同的map对象将按相同的顺序返回项。但是另一个与第一个相等的不同map对象可能会以不同的顺序返回它们。而在CLJS中的map几乎肯定会在不同的顺序返回值。

如果是编译器的bug,那么它可能在于协议实现中,这可能是一个严重的问题。

如果是这种情况,那就完全是另一回事了。

登录注册来回答此问题。

...