I’m updating some older Scala code to higher versions and can’t figure out how to construct a new TraversableOnce (Scala 2.11, 2.12) or IterableOnce (2.13+) from an existing instance of the same type. I do have the “scala-collection-compat” library dependency configured. There’s a method
private def sampleWithoutReplacement[A, CC[X] <: TraversableOnce[X]](xs: CC[A], k: Int)(implicit cbf: CanBuildFrom[CC[A], A, CC[A]]): CC[A] = {
that is called with an argument that is also a TraversableOnce. The usual error message is
Cannot construct a collection of type TraversableOnce[A] with elements of type A based on a collection of type TraversableOnce[A].
There’s code in the method that used to create and populate the new instance like this
// return collection of the right type
val builder = cbf(xs)
builder ++= reservoir
builder.result()
which is why the cbf is needed, but it’s not working in updated Scala, not even with BuildFrom rather than CanBuildFrom.
Does someone know what magic is needed?
scala> List(42).iterableFactory.from(Vector(27))
[[syntax trees at end of typer]] // rs$line$3
package <empty> {
final lazy module val rs$line$3: rs$line$3 = new rs$line$3()
final module class rs$line$3() extends Object() { this: rs$line$3.type =>
val res1: List[Int] =
List.apply[Int]([42 : Int]*).iterableFactory.from[Int](
Vector.apply[Int]([27 : Int]*))
}
}
val res1: List[Int] = List(27)
Where the companion knows how to build it. (Just guessing.)
The new instance can probably be created with something like
cbf.toFactory(xs).fromSpecific(reservoir)
However, I cannot call the method sampleWithoutReplacement for lack of a cbf which is causing the error message. Perhaps there is an import that I’m missing.
Below is a better code sample. The issue is getting from the method choice to sampleWithoutReplacement with an implicit BuildFrom. The comiler complains
Cannot construct a collection of type IterableOnce[A] with elements of type A based on a collection of type IterableOnce[A].
[error] sampleWithoutReplacement(xs, 1).iterator.next
for the code
package sample
import scala.collection.BuildFrom
object IterableOnceUtils {
def choice[A](xs: IterableOnce[A]): A = {
sampleWithoutReplacement(xs, 1).iterator.next
}
def sampleWithoutReplacement[A, CC[X] <: IterableOnce[X]](xs: CC[A], k: Int)(implicit cbf: BuildFrom[CC[A], A, CC[A]]): CC[A] = {
???
}
}
The solution might be to require that the initial call to choice() have a BuildFrom available and to pass it on to the next method. This seems to work:
package ai.lum.common
import scala.collection.BuildFrom
object IterableOnceUtils {
def choice[A, CC[X] <: IterableOnce[X]](xs: CC[A])(implicit cbf: BuildFrom[CC[A], A, CC[A]]): A = {
sampleWithoutReplacement(xs, 1)(cbf).iterator.next()
}
def sampleWithoutReplacement[A, CC[X] <: IterableOnce[X]](xs: CC[A], k: Int)(implicit cbf: BuildFrom[CC[A], A, CC[A]]): CC[A] = {
???
}
}
1 Like