Constructing a new TraverseableOnce or IterableOnce from existing instance

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