请在 2024 年 Clojure 状态调查中分享您的想法!

欢迎!请参阅 关于 页面以了解更多有关此服务的信息。

0
ClojureScript
已关闭

你好,
我遇到了一些与 sorted-map 相关的奇怪行为,这看起来像是一个bug或缺少文档。

具体来说,当创建一个具有不同类型键的排序映射,例如这样:(sorted-map :k1 :v1 "k2" :v2),然后进行如下查找:(:k1 (sorted-map :k1 :v1 "k2" :v2))会产生异常:Cannot compare k2 to :k1。这看起来是因为在 sorted-map 实现中使用了 compare

虽然对我来说最好是完全支持异构键,但似乎更合理的是要求排序映射的键是可以比较的,但据我看来,这一点应该在 文档 中说明。

即使确实有此要求,根据 Clojure 的总体设计精神,在查找时返回 nil 而不是抛出异常似乎更为合理。在我看来说,在向映射中添加不能与其他键比较的键时立即抛出异常要合理得多。您怎么看?

问候,
Simon

已关闭为重复问题:有点令人困惑的 sorted-set 行为
在插入元素时尝试抛出异常会要求扫描映射中所有现有元素以验证它们是否与您要插入的元素可比较,这将严重影响性能和可扩展性。同样,在引发比较和将其转换为 `nil` 时捕获异常也会增加不必要的开销。这并非 Clojure 中需要理解相关数据结构实际契约并正确使用它们的唯一情况,否则会得到意想不到的结果,牺牲性能。(更不用说还需要特殊 Clojure 定制化的所有底层 Java 类来实现这些功能。)

在这种情况下,涉及到的数据结构是 Java 的 `SortedMap` 接口。以下是 Java Doc 的第二段内容:

所有插入到有序映射中的键都必须实现 `Comparable` 接口(或被指定的比较器接受)。此外,所有这些键必须相互可比较:`k1.compareTo(k2)`(或 `comparator.compare(k1, k2)`)不能在有序映射中的任何键 `k1` 和 `k2` 上抛出 `ClassCastException`。违反此限制的尝试将导致引发 `ClassCastException` 的有问题方法或构造函数调用。

当您深入了解 JVM Clojure 时,不可避免地需要了解它基于并与之交互的 Java 类库的详细信息。也就是说,提供一个指向这个 Java Doc 的链接,以及 perhaps 将其概括/翻译成 Clojure 语汇的摘要,可能对 Clojure 文档字符串很有用。
我非常赞赏导致这种行为产生的非常实际的理由,并尊重它们。

关于开发人员体验和改进语言采用率的话题,尤其是与我提到的 clojurescript 有关,我会在回复 Alex 的回复中重申:存在“Comparable” JVM 契约是非常隐秘的,对于主要使用 Clojurescript 的人来说难以发现。我会建议在 sorted-map 文档中指向它。


我认为这样的契约应该是多“直观”是非常主观的,取决于每个开发者的背景;从一个不同的角度来看:如果我能从映射中取出我成功放入的每一对(键值),这是完全合理的。更进一步,即使可以直观地认为所有键之间必须能够存在某种顺序才能在有序映射上启用 `assoc` 和 `get`,但在没有更多文档的情况下,能够将不同类型的键相关联的事实,加剧了对(不正确的)默认比较器确实能够在不同类型之间提供顺序的信念。
我希望清楚地表明,我听了你的话,理解了你的观点,并对你所感到的困惑和挫折感表示同情!在文档中试图解决这个问题可能会起到一定的帮助,但我仍然预计人们会在这种事情上踩到地雷。我知道我肯定踩过,有时即使在有Java背景的情况下也可能会发生,这是一项巨大的挑战,试图构建一个内部一致的、优秀的语言,它存在于一个有着自己历史和不同哲学的巨大生态系统中。
> 尝试构建一个在有着自己历史和不同哲学的巨大生态系统中的、内部一致的、优秀的语言是一项巨大的挑战。

完全同意,即使是在完全拥有两个系统的情况下,保持一致性也是一项挑战。Clojure能够在其有两个(或更多)长期存在的生态系统的上层建立一致性语言,这是对在这一领域深入思考的一个证明,我很欣赏这一点。

1 答案

0

这是预期的行为(与Java在通过不处理查找键类型的比较器对map进行排序时查找键的行为一致)。


编辑
谢谢你的快速回复!
作为一个从Clojurescript进入Clojure生态系统的人,我的Clojure和Java背景都不够,所以这真的很不直观(因为语言的其他部分设计得很合理,旨在最小化惊喜)。

你考虑在sorted-map文档中解释这种行为吗?

大致是这样的:“`sorted-map` 允许你关联异构键,但不会允许查找。这与Java在通过不处理查找键类型的比较器对map进行排序时查找键的行为一致。”
...