I’m trying to figure out if I can convert List(Vector(1), Vector(2,3)) to Vector(List(1), List(2,3)) or any two traversable classes in a generic way.
This is what I have so far:
import collection.generic.CanBuildFrom
import collection.breakOut
import collection.mutable.ListBuffer
def f[A, CC[X] <: Traversable[X], DD[Y] <: Traversable[Y]](outer: DD[CC[A]])
(implicit cbf1: CanBuildFrom[Traversable[_], A, DD[A]],
cbf2: CanBuildFrom[Traversable[_], DD[A], CC[DD[A]]])
: CC[DD[A]] = {
val builder2 = cbf2()
outer.foreach { inner =>
val builder1 = cbf1()
builder1 ++= inner
builder2 += builder1.result
}
builder2.result
}
f(Vector(List(1), List(2,3)))(breakOut, breakOut)
f(ListBuffer(Vector(1), Vector(2,3)))(breakOut, breakOut)
If I remove the breakOuts, it will not compile:
f(Vector(List(1), List(2,3))) // won't work
could not find implicit value for parameter cbf1: scala.collection.generic.CanBuildFrom[Traversable[_],Int,scala.collection.immutable.Vector[Int]]
Are there implicits I can import? Any other way to approach this problem that would not require the breakOuts?
edit based on retronym’s answer:
import scalaz.CanBuildAnySelf
def f[A, CC[X] <: Traversable[X] : CanBuildAnySelf,
DD[Y] <: Traversable[Y] : CanBuildAnySelf](outer: DD[CC[A]])
: CC[DD[A]] = {
val builder2 = implicitly[CanBuildAnySelf[CC]].builder[DD[A], DD[A]].apply()
outer.foreach { inner =>
val builder1 = implicitly[CanBuildAnySelf[DD]].builder[A, A].apply()
builder1 ++= inner
builder2 += builder1.result
}
builder2.result
}
It’s nice and impressive to see that the CanBuildAnySelf context bound can be used completely separately.
We get it done in Scalaz, so it’s certainly possible.
We only consider ‘well-behaved’ collections, that is ones that can contain arbitrary element types, unlike
BitSet. This is witnessed with https://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/CanBuildAnySelf.scala