The next() Collections

Julien Richard-Foy , Scala Center

Scala.io – November 2, 2017

http://julienrf.github.io/2017/collections

Scala Center?

Outline

  1. What?
  2. Internal changes
  3. User-visible changes
  4. And then?

1 What?

Collections Redesign

Contributors

EPronovost
Ichoran
LPTK
NthPortal
SethTisue
allanrenucci
biboudis
esarbe
julienrf
marcelocenerine
nicolasstucki
odd
odersky
olafurpg
pnf
shawjef3
sjrd
smarter
stephennancekivell
szeiger
tpolecat
xavier-fernandez

2 Internal Changes

Outline

  1. What?
  2. Internal changes
    1. Class hierarchy
    2. Laziness
  3. User-visible changes
  4. And then?

2.1 Class Hierarchy

List Hierarchy 

List (Actual) Hierarchy 

List New Hierarchy 

Ops classes

trait IterableOps[A, CC[_], C] {

  def take(n: Int): C = …

  def map[B](f: A => B): CC[B] = …

}
trait List[A] extends Iterable[A] with IterableOps[A, List, List[A]]

Ops classes

trait IterableOps[A, CC[_], C] {

  def take(n: Int): C = …

  def map[B](f: A => B): CC[B] = …

}
trait StringOps extends IterableOps[Char, IndexedSeq, String]

2.2 Laziness

Current map Implementation

trait TraversableLike[+A, +Repr] {
  def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
    val b = bf(this)
    for (x <- this) b += f(x)
    b.result
  }
}

Current map Implementation on Views

trait TraversableViewLike[+A, +Repr] extends TraversableLike[A, Repr] {
  override def map[B, That](f: A => B)(implicit bf: CanBuildFrom[This, B, That]): That = {
    newMapped(f).asInstanceOf[That]
  }
}
object TraversableView {
  implicit def canBuildFrom[A] =
    new CanBuildFrom[TraversableView[_], A TraversableView[A]] {
      def apply() = new Builder[A, Nothing] {
        def +=(elem: A): this.type = this
        def result() = throw new UnsupportedOperationException("TraversableView.Builder.result")
      }
    }
}

Current Views’ Operations

New View-Based Implementations 

trait IterableOps[A, CC[_], C] {
  def map[B](f: A => B): CC[B] = fromIterable(View.Map(this, f))
}

Views

object View {
  case class Map[A, B](underlying: Iterable[A], f: A => B) extends View[B] {
    def iterator() = underlying.iterator().map(f)
  }
}

New View-Based Implementations 

trait IterableOps[A, CC[_], C] {
  def map[B](f: A => B): CC[B] = fromIterable(View.Map(this, f))
  def fromIterable[E](it: Iterable[E]): CC[E]
}

fromIterable 

trait List[+A] extends Iterable[A] with IterableOps[A, List, List[A]] {
  def fromIterable[E](it: Iterable[E]): List[E] =
    (List.newBuilder[E]() ++= it).result()
}
trait View[+A] extends Iterable[A] with IterableOps[A, View, View[A]] {
  def fromIterable[E](it: Iterable[E]): View[E] =
    new View[E] {
      def iterator() = it.iterator()
    }
}

New View-Based Implementations

3 User-Visible Changes

Outline

  1. What?
  2. Internal changes
  3. User-visible changes
    1. CanBuildFrom
    2. (Some) other (minor and random) changes
  4. And then?

3.1 CanBuildFrom

Current map Operation on List[A] 

Current map Operation on TreeMap[A, B] 

Current map Operation on TreeMap[A, B] 

CanBuildFrom is powerful 

trait GenTraversableLike[+A, +Repr] {
  def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That
}

One definition.

Works with String, List[A], TreeSet[A], HashMap[K, V], SortedMap[K, V].

(New) map Operation on List[A] 

(New) map Operation on TreeMap[K, V] 

But CanBuildFrom was more than that!

CanBuildFrom was one of the basic building block for generic programming over collections:

CanBuildFrom 

trait BuildFrom[From, A, C] {
  def fromSpecificIterable(from: From)(it: Iterable[A]): C
  def newBuilder(from: From): Builder[A, C]
}

CanBuildFrom 

trait BuildFrom[From, A, C] {
  def fromSpecificIterable(from: From)(it: Iterable[A]): C
  def newBuilder(from: From): Builder[A, C]
}
implicit class HasDistinctBy[A, CC[X] <: Iterable[X]](coll: CC[A]) {
  def distinctBy[B, C](f: A => B)(implicit bf: BuildFrom[CC[A], A, C]): C =
    bf.fromSpecificIterable(coll)(new View[A] {
      def iterator() = …
    })
}

Type-Driven Factories

trait Factory[-A, +C] extends Any {
  def fromSpecific(it: IterableOnce[A]): C
  def newBuilder(): Builder[A, C]
}

Type-Driven Factories

import play.api.libs.json._

implicit def iterableReads[A, C <: Iterable[A]](implicit
  factory: Factory[A, C],
  ra: Reads[A]
): Reads[C] = Reads[C] {
  case JsArray(elts) => JsResult.traverse(elts)(ra.reads)(factory)
  case _             => JsError("error.expected.jsarray")
}

val readsList = Reads.of[List[Int]]

val readsSortedMap = Reads.of[SortedMap[Instant, BigDecimal]]

3.2 Some Other (Minor and Random) Changes

No Traversable 

Collection Conversion

val xs = List(1 -> 'a', 2 -> 'b', 3 -> 'c')

val vector = xs.to(Vector) // Before: xs.to[Vector]
val map = xs.to(Map)       // Before: xs.toMap

groupMap 

case class User(name: String, age: Int)

// Before:
def namesByAge(users: Seq[User]): Map[Int, Seq[String]] =
  users.groupBy(_.age).mapValues(_.map(_.name))

// With `groupMap`:
def namesByAge(users: Seq[User]): Map[Int, Seq[String]] =
  users.groupMap(_.age)(_.name)

LazyList 

Current Tuple2Zipped 

scala> val xs = Vector(1, 2, 3)
xs: Vector[Int] = Vector(1, 2, 3)

scala> (xs, xs).zipped.map(_ + _)
res0: Vector[Int] = Vector(2, 4, 6)

Current Tuple2Zipped 

New Tuple2ZippedLazyZip2 

val xs = Vector(1, 2, 3)
(xs lazyZip xs).map(_ + _)

In Place Transformation Operations

val buffer = ArrayBuffer(1, 2, 3)
buffer.mapInPlace(x => x + 1)

4 And Then?

Move to scala-library

scala.Seq[A] = ? 

Migration tool

New Operations and Collections?

5 Questions?