25장 The Architecture of Scala Collections - codeport/scala GitHub Wiki

25. The Architecture of Scala Collections

Scala Collection은 코드 중복을 최소화했다.

25.1 Builders

Colleciton Operation은 foreach가 있는 Traversable과 Collection을 만드는 Buidler로 구현된다.

Traversable은 24장에서 다뤘고, Builders가 뭔지는 아래 두 예제를 보면 알 수 있다:

package scala.collection.generic

class Builder[-Elem, +To] {
  def +=(elem: Elem): this.type
  def result(): To
  def clear()
  def mapResult(f: To => NewTo): Builder[Elem, NewTo] = ...
}
package scala.collection

class TraversableLike[+Elem, +Repr] {
  def newBuilder: Builder[Elem, Repr] // deferred
  def foreach[U](f: Elem => U) // deferred
  ...
  def filter(p: Elem => Boolean): Repr = {
    val b = newBuilder
    foreach { elem => if (p(elem)) b += elem }
    b.result
  }
}

25.2 Factoring out common operations

"same-result-type" 원칙을 지키고 코드 중복을 회피하는데 사용하려고 implementation trait이라는 걸 사용한다. Like suffix가 달린 Trait이 Implementation Trait이다. TraversableLikeTraversable의 Implemtation Trait이다. 즉 Traversable Collection의 Operation은 TraversableLike에 구현하고 상속 받는다.

아래는 TraversableTraversableLike의 선언부이다:

trait TraversableLike[+Elem, +Repr] { ... }
trait Traversable[+A] extends TraversableLike[A, Traversable[A]] with GenTraversable[A] with TraversableOnce[A] with GenericTraversableTemplate[A, Traversable] { ...}

TraversableLike는 여러 Traversable Collection에서 재사용할 수 있도록 엘리먼트 타입(A)뿐만 아니라 Colection 타입(Traversable[A])도 파라미터로 받는다.

책에서 보여주는 예제를 보면 BigSet[Int]의 map 함수는 BigSet[Float]를 반환한다:

scala> val bits = BitSet(1, 2, 3)
bits: scala.collection.immutable.BitSet = BitSet(1, 2, 3)

scala> bits map (_.toFloat)
res14: scala.collection.immutable.Set[Float]
= Set(1.0, 2.0, 3.0)

즉, 타입 파라미터가 3개인 Builder가 필요한데 TraversableLike의 Builder는 타입 파라미터가 두 개다:

def newBuilder: Builder[Elem, Repr]

그래서 Scala는 타입 파라미터가 2개인 Builder를 생성하는 타입 파라미터 3개인 CanBuildFrom을 implicit 파라미터로 넘겨 받는다:

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

CanBuildFrom의 생김새:

package scala.collection.generic

trait CanBuildFrom[-From, -Elem, +To] {
  // Creates a new builder
  def apply(from: From): Builder[Elem, To]
}

CanBuildFrom의 쓰임새:

CanBuildFrom[Set[_], A, Set[A]]

25.3 Integrating new collections

지금까지 설명한 것으로 Collection을 하나 만들어 본다.