It appears that applying map
and filter
somehow converts a view
into a Seq
. The documentation contains this example:
> (v.view map (_ + 1) map (_ * 2)).force
res12: Seq[Int] = Vector(4, 6, 8, 10, 12, 14, 16, 18, 20, 22)
But if I do something similar, I get an error:
> 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]
It seems that if I map
over an Array
view
more than once the SeqView
becomes a Seq
.
> a.view.map(_+1)
res212: scala.collection.SeqView[Int,Array[Int]] = SeqViewM(...)
> a.view.map(_+1).map(_+1)
res211: Seq[Int] = SeqViewMM(...)
I suspect this behavior may have something to do with Array
being a mutable collection, as I cannot replicate this behavior with List
or Vector
. I can however filter
the Array
view
as many times as I like.
Pro tip: when debugging implicits in the REPL, reify
from reflect
is your friend.
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$1) => x$1.$plus(1)))(IndexedSeqView.arrCanBuildFrom).map(((x$2) => x$2.$times(2)))(Seq.canBuildFrom))
By design, IndexedSeqView.arrCanBuildFrom
produces not another IndexedSeqView
, but a plain old SeqView
. However, from then on you'd expect SeqView
s to remain so. In order for that to happen, the CBF
passed to the second map
should be SeqView.canBuildFrom
, but for some reason we are getting the one from Seq
. Now that we know the issue, let's pass SeqView.canBuildFrom
manually and dissect the error.
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)
^
Ok, so this is not a bug in implicit resolution or the compiler or anything; it is the library at fault, as the compiler is able to give us a good reason for failing here.
scalac
requires the second type param to CBF
be Int
or a supertype of it here, and since the one we give it takes any A
, we're good. The third one is unknown, so it can also be anything, also good. Therefore, the problem is in the first.
scala> implicitly[collection.SeqView[Int, _] <:< collection.TraversableView[_, _]]
<function1>
This narrows the issue down to Array[Int] <: Traversable[_]
. And there it is. Array
s are not Traversable
, so they fail here and are forced to become Seq
s by the CBF
in Seq
.
To fix this, SeqView
would need to have a arrCanBuildFrom
like IndexedSeqView
does. This is a bug in the library. This doesn't relate to Array
being mutable; it's really because Array
is not really a collection (it doesn't implement Traversable
) and has to be shoehorned in.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With