Extending versus wrapping Vector types

here’s my take: Scastie - An interactive playground for Scala.

abstract class SeqWrapper[Elem, Wrapper <: SeqWrapper[Elem, _]](elems: Elem*) {
  protected def wrap(rawCollection: Seq[Elem]): Wrapper
  protected def unwrap: Seq[Elem] = elems

  // below methods don't need to be overridden in subclasses
  def ++(other: Wrapper): Wrapper = wrap(unwrap ++ other.unwrap)
  def :+(elem: Elem): Wrapper = wrap(elems :+ elem)
}

case class Track(whatever: Int)

case class Tracks(tracks: Vector[Track]) extends SeqWrapper[Track, Tracks](tracks*) {
  def this(tracks: Track*) = this(Vector(tracks*))

  override protected def wrap(rawCollection: Seq[Track]): Tracks =
    Tracks(rawCollection.toVector)

  override protected def unwrap: Vector[Track] =
    tracks
}


@main def test() = {
  val tracks = new Tracks(Track(42), Track(27))
  assert(tracks ++ new Tracks(Track(5)) == tracks :+ Track(5))
  println(tracks :+ Track(5))
}

it would need some extra work to add a base class for companion objects to have the apply method to avoid using new keyword.

note that if you carefully manage the underlying sequence, so that it doesn’t change its implementation, then in methods like:

  override protected def wrap(rawCollection: Seq[Track]): Tracks =
    Tracks(rawCollection.toVector)

the .toVector will be no-op, as the rawCollection will be Vector already. maybe there’s a way to enforce it statically (on type-level?).

also note that the solution from @som-snytt is more lightweight as it doesn’t re-wrap the underlying sequence constantly.

1 Like