Take Home Points
In this chapter we looked at our first Scala collections: Lists and Ranges.
These are two types of sequence with different use cases:
Listsare convenient for storing finite collections of arbitrary values of a similar type;
Rangesprovide a more compact syntax for regular sequences of numbers.
We didn't cover this in the chapter but
List and Range have a common supertype, Seq.
This explains a few things:
ListandRangeboth have amapmethod, the behaviour of which is the same in each case.
- Doodle's n-ary combinators---
allAbove,allBeside, and so on---work equally well with sequences produced by transformingRanges, as they do sequences produced by transformingLists.
Scala has a large collections library containing many different useful types:
sequences, maps, and sets with mutable, immutable, lazy, and parallel variations.
These types form a single inheritance hierarchy
that provides consistent implementations of methods such as map, flatMap,
filter, find, and so on.
We looked at one kind of transform operation in this chapter: the map method.
map applies a user-specified function to every item in a sequence,
returning a new sequence of the results:
List(1, 2, 3).map(x => x * 2)
// res0: List[Int] = List(2, 4, 6)
The key point about map is that it only makes sense in a world
where we have functions that are also first class values.
The same can be said for many of the other cohort of combinators
such as filter, find, and flatMap.
First class functions allow us to pass operations to these methods as parameters,
separating user code from the implementational detail of allocating
temporary buffers.
Because many transformation methods return sequences, we can chain calls to perform complex transformations in a series of simple steps. Here is an example that demonstrates the power of thie approach:
// Print all even numbers from 1 to 100 that are also divisible by 3:
(1 to 50).toList.
map(x => x * 2).
filter(x => x % 3 == 0).
foreach(println)
// 6
// 12
// 18
// etc...
The structure of this computation looks similar to the structure of our Doodle programs: we build an intermediate representation by creating, combining, and transforming primitives, and perform side-effects at the end of the program. Obviously we've put less thought into this program than into Doodle, but it's interesting to note that even ad hoc functional programs can follow the same basic structure.