@@ -29,10 +29,29 @@ possible. (Ideally, everything should be defined in one place only,
2929but there are a few exceptions where things needed to be redefined.)
3030The design approach was to implement most operations in collection
3131"templates" that can be flexibly inherited from individual base
32- classes and implementations. The following pages explain these
33- templates and other classes and traits that constitute the "building
34- blocks" of the framework, as well as the construction principles they
35- support.
32+ classes and implementations.
33+
34+ More precisely, these templates address the following challenges:
35+
36+ - some transformation operations return the same concrete collection
37+ type (e.g. ` filter ` , called on a ` List[Int] ` returns a ` List[Int] ` ),
38+ - some transformation operations return the same concrete collection
39+ type with a different type of elements (e.g. ` map ` , called on a
40+ ` List[Int] ` , can return a ` List[String] ` ),
41+ - some collections have a single element type (e.g. ` List[A] ` ), while
42+ some others have two (e.g. ` Map[K, V] ` ),
43+ - some operations on collections return a different concrete collection
44+ depending on the element type. For example, ` map ` called on a ` Map `
45+ returns a ` Map ` if the mapping function returns a key-value pair, but
46+ otherwise returns an ` Iterable ` ,
47+ - transformation operations on some collections require additional
48+ implicit parameters (e.g. ` map ` on ` SortedSet ` takes an implicit
49+ ` Ordering ` ),
50+ - some collections are strict (e.g. ` List ` ), while some others are
51+ non-strict (e.g. ` View ` and ` LazyList ` ).
52+
53+ The following sections explain how the templates address these
54+ challenges.
3655
3756## Factoring out common operations ##
3857
@@ -210,7 +229,7 @@ In total, we’ve seen that we have four branches of template traits:
210229
211230
212231 kind | not sorted | sorted
213- ===========|===============|===============
232+ -----------|---------------|----------------
214233` CC[_] ` | ` IterableOps ` | ` SortedSetOps `
215234` CC[_, _] ` | ` MapOps ` | ` SortedMapOps `
216235
@@ -353,12 +372,12 @@ type of elements. The following code shows the relevant parts of `IterableOps` a
353372trait IterableOps [+ A , + CC [_], + C ] {
354373 def iterableFactory : IterableFactory [CC ]
355374 protected def fromSpecificIterable (coll : Iterable [A ]): C
356- protected def newSpecificBuilder () : Builder [A , C ]
375+ protected def newSpecificBuilder : Builder [A , C ]
357376}
358377
359378trait IterableFactory [+ CC [_]] {
360379 def from [A ](source : IterableOnce [A ]): CC [A ]
361- def newBuilder [A ]() : Builder [A , CC [A ]]
380+ def newBuilder [A ]: Builder [A , CC [A ]]
362381}
363382~~~
364383
@@ -379,23 +398,21 @@ types? In the next few sections you’ll be walked through three examples
379398that do this, namely capped sequences, sequences of RNA
380399bases and prefix maps implemented with Patricia tries.
381400
382- Say you want to create a collection containing * at most* ` n ` elements:
383- if more elements are added then the first elements are removed. Such a
384- collection can be efficiently implemented using an ` Array ` with a fixed
385- capacity: random access and element insertion are O(1) operations.
401+ Say you want to create an immutable collection containing * at most* ` n ` elements:
402+ if more elements are added then the first elements are removed.
386403
387404The first task is to find the supertype of our collection: is it
388405` Seq ` , ` Set ` , ` Map ` or just ` Iterable ` ? In our case, it is tempting
389406to choose ` Seq ` because our collection can contain duplicates and
390407iteration order is determined by insertion order. However, some
391- properties of ` Seq ` are not satisfied:
408+ properties of [ ` Seq ` ] ( /overviews/collections/seqs.html ) are not satisfied:
392409
393410~~~ scala
394411(xs ++ ys).size == xs.size + ys.size
395412~~~
396413
397414Consequently, the only sensible choice as a base collection type
398- is ` Iterable ` .
415+ is ` collection.immutable. Iterable` .
399416
400417### First version of ` Capped ` class ###
401418
@@ -688,7 +705,7 @@ To start with the second example, we define the four RNA Bases:
688705 val toInt: Base => Int = Map(A -> 0, U -> 1, G -> 2, C -> 3)
689706 }
690707
691- Say you want to create a new sequence type for RNA strands, which are
708+ Say you want to create a new immutable sequence type for RNA strands, which are
692709sequences of bases A (adenine), U (uracil), G (guanine), and C
693710(cytosine). The definitions for bases are easily set up as shown in the
694711listing of RNA bases above.
@@ -1087,7 +1104,7 @@ reduced.
10871104
10881105## Integrating a new prefix map ##
10891106
1090- As a third example you'll learn how to integrate a new kind of map
1107+ As a third example you'll learn how to integrate a new kind of mutable map
10911108into the collection framework. The idea is to implement a mutable map
10921109with ` String ` as the type of keys by a "Patricia trie". The term
10931110* Patricia* is in fact an abbreviation for "Practical Algorithm to
@@ -1124,12 +1141,12 @@ define a prefix map with the keys shown in the running example:
11241141
11251142 scala> val m = PrefixMap("abc" -> 0, "abd" -> 1, "al" -> 2,
11261143 "all" -> 3, "xy" -> 4)
1127- m: PrefixMap[Int] = Map ((abc,0), (abd,1), (al,2), (all,3), (xy,4))
1144+ m: PrefixMap[Int] = PrefixMap ((abc,0), (abd,1), (al,2), (all,3), (xy,4))
11281145
11291146Then calling ` withPrefix ` on ` m ` will yield another prefix map:
11301147
11311148 scala> m withPrefix "a"
1132- res14: PrefixMap[Int] = Map ((bc,0), (bd,1), (l,2), (ll,3))
1149+ res14: PrefixMap[Int] = PrefixMap ((bc,0), (bd,1), (l,2), (ll,3))
11331150
11341151### Patricia trie implementation ###
11351152
@@ -1334,6 +1351,9 @@ into the framework you need to pay attention to the following points:
133413512 . Pick the right base traits for the collection.
133513523 . Inherit from the right implementation trait to implement most
13361353 collection operations.
1354+ 4 . Overload desired operations that do not return, by default, a
1355+ collection as specific as they could. A complete list of such
1356+ operations is given as an appendix.
13371357
13381358You have now seen how Scala's collections are built and how you can
13391359add new kinds of collections. Because of Scala's rich support for
@@ -1346,3 +1366,49 @@ These pages contain material adapted from the 2nd edition of
13461366[ Programming in Scala] ( http://www.artima.com/shop/programming_in_scala ) by
13471367Odersky, Spoon and Venners. We thank Artima for graciously agreeing to its
13481368publication.
1369+
1370+ ## Appendix: Methods to overload to support the “same result type” principle ##
1371+
1372+ You want to add overloads to specialize a transformation operations such that they return a more specific result. Examples are:
1373+ - ` map ` , on ` StringOps ` , when the mapping function returns a ` Char ` , should return a ` String ` (instead of an ` IndexedSeq ` ),
1374+ - ` map ` , on ` Map ` , when the mapping function returns a pair, should return a ` Map ` (instead of an ` Iterable ` ),
1375+ - ` map ` , on ` SortedSet ` , when an implicit ` Ordering ` is available for the resulting element type, should return a
1376+ ` SortedSet ` (instead of a ` Set ` ).
1377+
1378+ The following table lists transformation operations that might return a too wide type. You might want to overload
1379+ these operations to return a more specific type.
1380+
1381+ Collection | Operations
1382+ ---------------|--------------
1383+ ` Iterable ` | ` map ` , ` flatMap ` , ` collect ` , ` scanLeft ` , ` scanRight ` , ` groupMap ` , ` concat ` , ` zip ` , ` zipAll ` , ` unzip `
1384+ ` Seq ` | ` prepended ` , ` appended ` , ` prependedAll ` , ` appendedAll ` , ` padTo ` , ` patch `
1385+ ` immutable.Seq ` | ` updated `
1386+ ` SortedSet ` | ` map ` , ` flatMap ` , ` collect ` , ` zip `
1387+ ` Map ` | ` map ` , ` flatMap ` , ` collect ` , ` concat `
1388+ ` immutable.Map ` | ` updated ` , ` transform `
1389+ ` SortedMap ` | ` map ` , ` flatMap ` , ` collect ` , ` concat `
1390+ ` immutable.SortedMap ` | ` updated `
1391+
1392+ ## Appendix: Cross-building custom collections ##
1393+
1394+ Since the new internal API of the Scala 2.13 collections is very different from the previous
1395+ collections API, authors of custom collection types should use separate source directories
1396+ (per Scala version) to define them.
1397+
1398+ With sbt you can achieve this by adding the following setting to your project:
1399+
1400+ ~~~ scala
1401+ // Adds a `src/main/scala-2.13+` source directory for Scala 2.13 and newer
1402+ // and a `src/main/scala-2.13-` source directory for Scala version older than 2.13
1403+ unmanagedSourceDirectories in Compile += {
1404+ val sourceDir = (sourceDirectory in Compile ).value
1405+ CrossVersion .partialVersion(scalaVersion.value) match {
1406+ case Some ((2 , n)) if n >= 13 => sourceDir / " scala-2.13+"
1407+ case _ => sourceDir / " scala-2.13-"
1408+ }
1409+ }
1410+ ~~~
1411+
1412+ And then you can define a Scala 2.13 compatible implementation of your collection
1413+ in the ` src/main/scala-2.13+ ` source directory, and an implementation for the
1414+ previous Scala versions in the ` src/main/scala-2.13- ` source directory.
0 commit comments