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

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

0
Clojure

使用 subvec 从 PersistentVector 创建的子向量不能成为 transient

user=> (transient (subvec [1 2 3 4] 2)) ClassCastException clojure.lang.APersistentVector$SubVector cannot be cast to clojure.lang.IEditableCollection clojure.core/transient (core.clj:2864)

原因: {{APersistentVector$SubVector}} 没有实现 {{IEditableCollection}}

补丁: CLJ-787-p1.patch

方法: 根据 {{TransientVector}} 创建一个 {{TransientSubVector}}。

两个假设
对于 {{TransientSubVector}} 将 {{ensureEditable}} 功能委派给底层 {{TransientVector}} 是可以的(有时显式,有时隐式)——显式调用 {{ensureEditable}} 还需要底层数组的字段是具体的 {{TransientVector}} 类型,而不是 {{ITransientVector}} 接口。 当在一个持久向量上抛出异常的操作从错误的线程发生(或持久化后)时,我们抛出这个异常,而不是 transient 在不适当访问时抛出的 {{IllegalAccessError}}。

8 个答案

0

评论者:stuart.sierra

已确认。APersistentVector$SubVector 没有实现 IEditableCollection。

当前 TransientVector 的实现依赖于 PersistentVector 的实现细节,因此这不是一个简单的修复。最简单的修复 可能是在 SubVector 中实现 IEditableCollection.asTransient,通过创建一个新的 PersistentVector,但我不知道性能影响。

0

评论者:gfredericks

我们可以通过基于基本TransientVector创建TransientSubVector来获得与SubVector相同的性能特征,对吧?

准备一个相应的补丁。

0

评论者:gfredericks

提交消息中的文本

做出了两个假设

  • 让TransientSubVector将ensureEditable的功能委托给基本TransientVector是可以的(有时明确,有时隐式)-- 明确调用ensureEditable还要求基础向量的字段是具体的TransientVector类型,而不是ITransientVector接口。
    当在错误的线程(或在持久化之后)从PersistentVector发生会抛出异常的操作时,我们抛出该异常,而不是像非持久型结构在不适当地访问时抛出的IllegalAccessError。
    TransientSubVector期望在任何实现了IPersistentVector的对象上工作。请注意,这包括MapEntry和LazilyPersistentVector等具体类型,也可能包括任何用户实现的IPersistentVector类型。TransientSubVector假设在SubVector问题中的IPersistentVector也是IEditableCollection(可以被转换为 transient 类型)。请注意,虽然PersistentVector实现了TransientVector和IEditableCollection,但APersistentVector没有实现。要真正在SubVector中同时实现这一点,我认为你需要保证IPersistentVector扩展了IEditableCollection,但我认为这不是我们想要的。
    我不会看到一个简单的解决方案。每次我看到所有这些修饰符(Transient、Sub等)在不同的组合中被创建,这清楚地表明不同的功能类型正在被重新组合到单一继承的OO树中。你可以在大多数集合库中看到相同的事情(甚至连Java的都必须要有ConcurrentIdentitySortedMap也太小气了!)。
    需要更多的思考。
  • 评论者:jafingerhut
    我认为这个补丁对这个类结构的假设有误,这个结构可以说非常复杂。
    以下是一个反例,强调了APersistentVector的几个子类型之一(如MapEntry)不是PersistentVector
    user=> (transient (subvec (first {:a 1}) 0 1))
    ClassCastException clojure.lang.MapEntry cannot be cast to clojure.lang.IEditableCollection  clojure.lang.APersistentVector$TransientSubVector.<init> (APersistentVector.java:592)
0
PersistentVector.SubVector期望在所有实现了IPersistentVector的对象上工作。请注意,这包括MapEntry和LazilyPersistentVector等具体类型,但也可能包括任何用户实现的IPersistentVector类型。TransientSubVector假设在SubVector问题中的IPersistentVector也是IEditableCollection(可以被转换为 transient)。请注意,虽然PersistentVector实现了TransientVector和IEditableCollection,但APersistentVector没有实现。要真正在SubVector中同时实现这一点,我认为你需要保证IPersistentVector扩展了IEditableCollection,但我认为这不是我们想要的。
需要更多的思考。



评论者:alexmiller


我认为在这个补丁中有一些关于类结构的假设并不成立。结构本身就是相当复杂的。
反例之一是强调APersistentVector的一些子类型(如MapEntry)不是PersistentVector


user=> (transient (subvec (first {:a 1}) 0 1))

ClassCastException clojure.lang.MapEntry cannot be cast to clojure.lang.IEditableCollection  clojure.lang.APersistentVector$TransientSubVector.<init> (APersistentVector.java:592)

PersistentVector.SubVector期望在任何实现了IPersistentVector的对象上工作。请注意,这包括MapEntry和LazilyPersistentVector等具体类型,但也可能包括任何用户实现的IPersistentVector类型。TransientSubVector假设在SubVector问题中的IPersistentVector也是IEditableCollection(可以被转换为 transient)。请注意,虽然PersistentVector实现了TransientVector和IEditableCollection,但APersistentVector没有实现。要真正在SubVector中同时实现这一点,我认为你需要保证IPersistentVector扩展了IEditableCollection,但我认为这不是我们想要的。
0
我没有看到简单的解决方案。每次我看到所有这些修饰符(Transient、Sub等)在不同的组合中被创建,这清楚地表明不同的功能类型正在被重新组合到单一继承的OO树中。你可以在大多数集合库中看到相同的事情(甚至连Java的都必须要有ConcurrentIdentitySortedMap也太小气了!)。

需要更多的思考。

0

评论者:alexmiller

目前没有很好的解决方案可以考虑,从1.6版本中删除。

0

评论者:alexmiller

Rich 将其移至已审查,但我觉得应该保留为不完整,因为最后的评论从未被解决。

0
参考: https://clojure.atlassian.net/browse/CLJ-787 (由aredington报告)
...