I am trying to create a wrapper of List
with a specific type (e.g. List[Int]
) such that methods that take an implicit CanBuildFrom
par
As far I know from reading this article:
http://www.scala-lang.org/docu/files/collections-api/collections-impl.html
your solution is the simplest one, if you want filter/map/etc. to all return instances of MyList
. newBuilder
is needed for ops like filter
, and the implicit CanBuildFrom
for ops like map
, which may change the collection type.
What you should maybe do in your CanBuildFrom
is this:
def apply(from: MyList) = from.newBuilder // call it on `from'
which ensures that a map
on a statically-typed MyList
which actually has a dynamic type that is a subtype of MyList
will reuse that same dynamic type.
Edit: seems like there is a little something missing, for this map
returns an instance of List
and not MyList
:
val l1: LinearSeq[Int] = new MyList(List(1, 2, 3))
println(l1.map(_ + 1)) // prints List(2, 3, 4)
it looks like this is also the case with the RNA
example taken from the linked article. If it has static type IndexedSeq[Base]
instead of RNA
, a map
on it returns a vector.
Edit 2: looks like this is a more general problem, discussed in this question.
Regarding my follow-up question how to mixin the wrapper logic via classes or traits, this is what I came up with:
import scala.collection._
trait ListWrapper[Elem, Repr <: ListWrapper[Elem, Repr]]
extends immutable.LinearSeq[Elem]
with LinearSeqLike[Elem, Repr]
with generic.SeqForwarder[Elem] { self: Repr =>
def wrapperCompanion: ListWrapperCompanion[Elem, Repr]
override def newBuilder: mutable.Builder[Elem, Repr] =
wrapperCompanion.newBuilder
}
trait ListWrapperCompanion[Elem, Repr <: ListWrapper[Elem, Repr]] {
def apply(elems: TraversableOnce[Elem]): Repr
def newBuilder: mutable.Builder[Elem, Repr] =
new mutable.ListBuffer[Elem].mapResult(apply)
def canBuildFromWrapper: generic.CanBuildFrom[Repr, Elem, Repr] = {
new generic.CanBuildFrom[Repr, Elem, Repr] {
def apply(from: Repr) = from.newBuilder
def apply() = newBuilder
}
}
}
Now MyList
can be implemented by:
class MyList(val underlying: List[Int]) extends ListWrapper[Int, MyList] {
def wrapperCompanion = MyList
}
object MyList extends ListWrapperCompanion[Int, MyList] {
def apply(elems: TraversableOnce[Int]) = new MyList(elems.toList)
implicit def canBuildFrom = canBuildFromWrapper
}
This is definitely better than having all the boilerplate code in MyList
's definition, but it is still a lot to write for making MyList
just a wrapper for List
.