Scala `view`:`force` 不是 `Seq` 的成员

Scala `view`: `force` is not a member of `Seq`

似乎应用 mapfilter 以某种方式将 view 转换为 Seqdocumentation 包含此示例:

> (v.view map (_ + 1) map (_ * 2)).force
res12: Seq[Int] = Vector(4, 6, 8, 10, 12, 14, 16, 18, 20, 22)  

但是如果我做类似的事情,我会得到一个错误:

> val a = Array(1,2,3)
> s.view.map(_ + 1).map(_ + 1).force
<console>:67: error: value force is not a member of Seq[Int]

似乎如果我 map 超过 Array view 不止一次 SeqView 变成 Seq.

> a.view.map(_+1)
res212: scala.collection.SeqView[Int,Array[Int]] = SeqViewM(...)
> a.view.map(_+1).map(_+1)
res211: Seq[Int] = SeqViewMM(...)

我怀疑这种行为可能与 Array 是一个可变集合有关,因为我无法用 ListVector 复制这种行为。但是,我可以 filter Array view 任意多次。

专业提示:在 REPL 中调试隐式时,来自 reflectreify 是你的朋友。

scala> import reflect.runtime.universe.reify
scala> import collection.mutable._ // To clean up reified exprs
scala> reify(a.view.map(_ + 1).map(_ * 2))
Expr[Seq[Int]](Predef.intArrayOps($read.a).view.map(((x) => x.$plus(1)))(IndexedSeqView.arrCanBuildFrom).map(((x) => x.$times(2)))(Seq.canBuildFrom))

根据设计,IndexedSeqView.arrCanBuildFrom 不会生成另一个 IndexedSeqView,而是生成一个普通的旧 SeqView。但是,从那时起您希望 SeqViews 保持不变。为了实现这一点,传递给第二个 mapCBF 应该是 SeqView.canBuildFrom,但出于某种原因我们从 Seq 获得了一个。现在我们知道了这个问题,让我们手动传递 SeqView.canBuildFrom 并剖析错误。

scala> a.view.map(_ + 1).map(_ * 2)(collection.SeqView.canBuildFrom)
<console>:??: error: polymorphic expression cannot be instantiated to expected type;
 found   : [A]scala.collection.generic.CanBuildFrom[collection.SeqView.Coll,A,scala.collection.SeqView[A,Seq[_]]]
    (which expands to)  [A]scala.collection.generic.CanBuildFrom[scala.collection.TraversableView[_, _ <: Traversable[_]],A,scala.collection.SeqView[A,Seq[_]]]
 required: scala.collection.generic.CanBuildFrom[scala.collection.SeqView[Int,Array[Int]],Int,?]
       a.view.map(_ + 1).map(_ * 2)(collection.SeqView.canBuildFrom)
                                                       ^

好的,所以这不是隐式解析或编译器或任何东西中的错误;这是库的错误,因为编译器能够为我们提供失败的充分理由。

scalac 要求 CBF 的第二个类型参数是 Int 或者它的超类型,因为我们给它的那个接受任何 A,我们很好。第三个是未知的,所以也可以是任何东西,也很好。所以,问题在第一个。

scala> implicitly[collection.SeqView[Int, _] <:< collection.TraversableView[_, _]]
<function1>

这将问题缩小为 Array[Int] <: Traversable[_]。它就在那里。 Array 不是 Traversable,所以它们在这里失败并被 Seq 中的 CBF 强制变成 Seq

要解决这个问题,SeqView 需要像 IndexedSeqView 那样有一个 arrCanBuildFrom。这是库中的错误。这与 Array 可变无关;这真的是因为 Array 不是真正的集合(它没有实现 Traversable)并且必须硬塞进去。