Memo after reading Scala for the Impatient

Catalogue
  1. 1. Chapter 1 The Basics
    1. 1.1. 1.3 Commonly Used Types
    2. 1.2. Arithmetic and Operator Overloading
    3. 1.3. Input and Output
  2. 2. Chapter 2 Control Structure and Functions
    1. 2.1. 2.5 Loops
    2. 2.2. 2.7 Functions
    3. 2.3. 2.10 Procedures
    4. 2.4. 2.11 Exceptions
  3. 3. Chapter 3 Working with Arrays
    1. 3.1. 3.2 Variable-length Arrays: Array Buffers
    2. 3.2. 3.4 Transforming Arrays
    3. 3.3. 3.5 Common Algorithms
    4. 3.4. 3.6 Deciphering Scaladoc
    5. 3.5. 3.7 Multidimensional Arrays
      1. 3.5.1. Exercise
  4. 4. Chapter 4 Maps and Tuples
    1. 4.1. 4.1 Constructing a Map
    2. 4.2. 4.2 Accessing Map Values
    3. 4.3. 4.4 Iterating over Maps
  5. 5. Chapter 5 Classes
    1. 5.1. Properties with Getters and Setters
    2. 5.2. 5.4 Object-private Fields
    3. 5.3. 5.5 Constrcutors
    4. 5.4. 5.8 Nested Classes
  6. 6. Chapter 6 Objects
    1. 6.1. 6.1 Singletons
    2. 6.2. 6.2 Companion Object
    3. 6.3. 6.4 The apply method
    4. 6.4. 6.5 Application Objects
  7. 7. Packages and Imports
    1. 7.1. 7.2 Scope Rules
    2. 7.2. 7.3 Chained Package Clauses
    3. 7.3. 7.4 Top-of-File Notation
    4. 7.4. 7.5 Package Objects
    5. 7.5. 7.9 Renaming and Hiding Members
  8. 8. Chapter 8 Inheritance
    1. 8.1. 8.2 Override and Invoke Super Method
    2. 8.2. 8.3 Type Checks and Casts

This is a memo for reading the book “Scala for the Impatient”

Chapter 1 The Basics

1.3 Commonly Used Types

Like Java, Scala has 7 numeric types: Byte, Char, Short, Int, Long, Float, and Double. Unlike Java, these types are classes, you can invoke methods on numbers.

1
"Hello".intersect("World") // Yields "lo"

In this expression, the java.lang.String object “Hello” is implicitly converted to a StringOps object, and then the intersect method of the StringOps class is applied. So remember to look into the StringOps class when you use the Scala docs.
Similarly, there are RichInt, RichDouble

Arithmetic and Operator Overloading

1
counter += 1 // Increments counter -- Scala has no ++

Input and Output

1
2
println("Answer: " + 42)
printf("Hello, %s! You are %d years old.\n", "Apple", 42)

Chapter 2 Control Structure and Functions

2.5 Loops

until keyword

1
2
3
val s = "Hello"
for (i <- 0 until s.length) // last value = s.length - 1
sum += i

break out of loops
Scala has no break or continue statements to break out of a loop. Here are a few options for break:

  1. Use a Boolean control variable
  2. Use nested functions - return from the middle of a function
  3. Use the break method in the Breaks object: import scala.until.control.Breaks._

Try to avoid 3) if possible

advanced for loops

1
2
for (i <- 1 to 3; j <- 1 to 3 if i != j) print((10*i*j)+" ")
// 12 13 21 23 31 32

advanced for comprehensions
The generated collection is compatible with the first generator

1
2
3
4
for (c <- "hoho"; i <- 0 to 1) yield (c + i).toChar
// hiophiop
for (i <- 0 to 1; c <- "hoho") yield (c + i).toChar
// Vector(h, o, h, o, i, p, i, p)

2.7 Functions

As long as the function is not recursive, you don’t have to specify the return type.

Variable Arguments

1
2
3
4
5
def sum(args: Int*) = {
var result = 0
for (arg <- args) result += arg
result
}

Now, you can pass as many arguments as you want: sum(1,2,3,4). However, you can’t pass a sequence to it. The remedy is to tell the compiler that you want the parameter to be considered as an argument sequence. Append : * like this `sum(1 to 5: *)`

1
2
sum(1, 2, 3, 4) // 10
sum(1 to 4: _*) // 10

This call syntax is needed in a recursive definition

1
2
3
4
def recursiveSum(args: Int*): Int = {
if (args.length == 0) 0
else args.head + recursiveSum(args.tail: _*)
}

2.10 Procedures

If a function body is enclosed in braces w/o a proceeding = symbol, then the return type is Unit. Such a function is called a procedure.

2.11 Exceptions

Scala exceptions work the same way as in Java. When you throw an exception,

1
throw new IllegalArgumentException("x should be positive")

the current computation is aborted, and the runtime system looks for an exception handler that can accept an IllegalArgumentException. Control resumes with the innermost such handler. If no such handler exists, the program terminates.

A throw expr has the special type Nothing. This is useful in if/else expr. If one branch has type Nothing, the type of the if/else expr is the type of the other branch.

The syntax for catching exceptions:

1
2
3
4
5
6
try {
process(new URL("http://horsmann.com/fred-tiny.gif"))
} catch {
case _: MalformedURLException => println("Bad URL: " + url)
case ex: IOException => ex.printStackTrace()
}

Note that the more general exception types should come after the more specific ones.

Chapter 3 Working with Arrays

3.2 Variable-length Arrays: Array Buffers

1
2
3
4
5
6
7
8
9
10
11
import scala.collection.mutable.ArrayBuffer
val b = ArrayBuffer[Int]()

b += 1
// Add an element at the end
b += (1, 2, 3, 5)
// add multiple elements at the end
b ++= Array(6, 7, 8)
// You can append any collection with the ++= operator
b.trimEnd(5)
// removes the last 5 elements

Adding or removing elements at the end of an array buffer is an efficient operation

3.4 Transforming Arrays

1
a.filter(_ % 2).map(2 * _)

3.5 Common Algorithms

sorted sorts an array or array buffer and returns a new, w/o modifying the original

1
2
val b = ArrayBuffer(1, 7, 2, 9)
val bSorted = b.sorted(_ < _)

You can sort an array, but not an array buffer, in place:

1
2
val a = Array(1, 7, 2, 9)
scala.util.Sorting.quickSort(a)

mkString

1
2
3
4
5
6
7
a.mkString("<", ",", ">")
// "<1,2,7,9>"
/* Contrast with toStirng */
a.toString
// "[I@b24d"
b.toString
// "ArrayBuffer(1, 7, 2, 9)"

3.6 Deciphering Scaladoc

def appendAll(xs: TraversableOnce[A]): Unit

The xs parameter can be any collection with the TraversableOnce trait, the most general trait in the scala collection hierarchy. Just think of any collection when you see TraversableOnce, Traversable, and Iterable

When you see Seq, this trait requires element access by an integer index, so think of array, list or string

3.7 Multidimensional Arrays

Multidimensional arrays are implemented as arrays of arrays

1
2
3
4
val mx = Array.ofDim[Double](3, 4)
val row = 2
val column = 2
mx(row)(column) = 42
Exercise

Q2. write a loop that swaps adjacent elements of an array of integers. Array(1,2,3,4,5) => Array(2,1,4,3,5)

1
2
3
def swapEveryTwo(a: Array[Int]) = {
a.grouped(2).flatMap(_.reverse).toArray
}

Q3. Repeat 2), but produce a new array with the swapped values. Use for/yield

1
for (i <- 0 until (a.length, 2); j <- (i+1) to (i, -1) if (j < a.length)) yield a(j)

Chapter 4 Maps and Tuples

4.1 Constructing a Map

The default Map is immutable. To use a mutable map,

1
2
import scala.collection.mutable.Map as mutMap
val scores = mutMap(("Alice", 10), ("Bob", 20))

In a mutable map, you can update a map value, or add a new one.

1
2
3
4
scores("Bob") = 5
scores("Fred") = 2
scores += ("Bob" -> 7, "Fred" -> 2)
scores -= "Alice"

4.2 Accessing Map Values

1
val bobsScore = scores.getOrElse("Bob", 0)

If you use scores.get the return type will be an Option object, that ie either Some(value for key) or None.

4.4 Iterating over Maps

1
2
3
for ((k, v) <- map) process k and v
scores.keySet
scores.values

Chapter 5 Classes

  • Fields in classes automatically come with getters and setters
  • You can replace a field with a custom getter/setter w/o changing the client of a class
  • Use the @BeanProperty annotation to generate the JavaBeans getXxx/set Xxx methods
  • It is considered good style to use () for a mutator method, and to drop () for an accessor method

Properties with Getters and Setters

1
2
3
4
5
class Person {
var age = 0
}
println(fred.age)
fred.age = 21

Scala generates a class for the JVM with a private age field and getter and setter methods. These methods are public because we did not declare age as private

At any time, you can redefine the getter and setter methods yourself. For example,

1
2
3
4
5
6
7
8
9
class Person {
private var privateAge = 0

def age = privateAge // this is the getter method
def age_ =(newValue: Int) { // this is the setter method
if (newValue > privateAge) privateAge = newValue;
// can't get younger
}
}

If a field is val, only a getter method will be initiated.

5.4 Object-private Fields

In scala, a method can access the private fields of all objects of its class. For example

1
2
3
4
5
class Counter {
private var value = 0
def increment() { value += 1}
def isLess(other: Counter) = value < other.value
}

If you don’t want this, scala allows an even more severe access restriction, with private[this] var value = 0. Now, the methods of the Counter class can only access the value field of the current object, not others. And no getter and setter is generated for that field.

Scala allows you to grant access rights to specific classes. private[ClassName] will give specific class access to the field.

5.5 Constrcutors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Here is a class with auxiliary constructors
class Person {
private var name= ""
private var age = 0

def this(name: String) { // An auxiliary constructor
this() // Calls primary constructor
this.name = name
}

def this(name: String, age: Int) {
this(name) // Calls previous auxiliary constructor
this.age = age
}
}

Each auxiliary constructors must start with a call to previously defined auxiliary constructor or the primary constructor

Primary Constructor

  1. The parameters of the primary constructor are placed immediately after the class name
  2. The primary constructor executes all statements in the class definition
1
2
3
4
class Person(val name: String, val age: Int) {
println("Just constructed another person")
def desc = name + " is " + age + " years old"
}

the println is part of the primary constructor.

You can often eliminate auxiliary constructors by using default arguments in the primary constructor: class Person(val name: String = "", val age: Int = 0)

To make the primary constructor private, place the keyword private like this: class Person private(val id: Int){...}. Then a class user must use an auxiliary constructor to construct a Person object.

5.8 Nested Classes

In scala, you can nest just about anything inside anything.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import scala.collection.mutable.ArrayBuffer
class Network {
class Member(val name: String) {
val contacts = new ArrayBuffer[Member]
}

private val members = new ArrayBuffer[Member]

def join(name: String) {
val m = new Member(name)
members += m
}
}

val chatter = new Network
val worker = new Network

chatter.Member and worker.Member are different classes. Because in scala, each instance has its own class Member. In our network example, you can add a member within its own network, but not across networks.

1
2
3
4
5
val fred = chatter.join("Fred")
val wilma = chatter.join("Wilma")
fred.contacts += wilma
val barney = worker.join("Barney")
fred.contacts += barney // No!

If you don’t want such behaviour, a good workaround is to place the Member definition to the Network companion object.

Chapter 6 Objects

6.1 Singletons

Scala has no static methods or fields. Instead, you use the object constructor. The constructor of an object is executed when the object is first used. If an object is never used, its constructor is not executed.

An object can have essentially all the features of a class - it can even extend other classes or traits. Just one exception: You cannot provide constructor parameters.

  • As a home for utility functions or constants
  • when a single immutable instance can be shared efficiently
  • when a single instance is required to coordinate some service

6.2 Companion Object

1
2
3
4
5
6
7
8
9
10
11
class Account {
val id = Account.newUniqueNumber()
private var balance = 0.0
def deposit(amount: Double) { balance += amount }
...
}

object Account { // the companion object
private var lastNumber = 0
private def newUniqueNumber() = { lastNumber += 1; lastNumber }
}

The class and its companion object can access each other’s private features. They must be located in the same source file

Note: the companion object is accessible, but it is not in scope. Need to call Account.newUniqueNumber() and not newUniqueNumber() directly.

6.4 The apply method

It is common to have objects with an apply method. Typically, such an apply method returns an object of the companion class. But why doesn’t one just use a constructor? Well, not having the new keyword is handy for nested expressions, such as Array(Array(1, 7), Array(2))

Caution: It is easy to confuse Array(100) and new Array(100). The 1st yields an Array[Int] but the 2nd returns an Array[Nothing] with 100 null elements

6.5 Application Objects

Each scala program must start with an object’s main method of type Array[String] => Unit. Instead of providing a main method for your application, you can extend App trait and place the program code into the constructor body.

Packages and Imports

7.2 Scope Rules

You can access names from the enclosing scope. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com {
package horstmann {
object Utils {
def percentOf(...)
}

package impatient {
class Employee {
...
def giveRaise(..) {
salary += Utils.percentOf(salary, rate) // Here~
}
}
}
}
}

Everything in the parent package is in scope.

7.3 Chained Package Clauses

A package clause can contain a “chain”

1
2
3
4
5
6
7
package com.horstmann.impatient {
// Members of com and com.horstmann are not visible here!!!
package people {
class Person
...
}
}

7.4 Top-of-File Notation

1
2
3
4
package com.horstmann.impatient
package people

class Person

This is equivalent to 7.3

Note: everything in the file belongs to com.horstmann.impatient.people, but the package com.hostmann.impatient has also been opened up so you can refer to its contents.

7.5 Package Objects

A package can contain classes, objects, and traits, but not the definitions of functions or variables. That’s an unfortunate limitation of the Java virtual machine. Now, package objects address this limitation.
Every package can have one package object. You define it in the parent package, and it has the same name as the child package. For example

1
2
3
4
5
6
7
8
9
10
11
package com.horstmann.impatient

package object people {
val defaultName = "John"
}

package people {
class Person {
var name = defaultName
}
}

Usually, the package objects is out in com/hostmann/impatient/people/package.scala

7.9 Renaming and Hiding Members

1
import java.util.{HashMap => JavaHashMap}

Chapter 8 Inheritance

8.2 Override and Invoke Super Method

1
2
3
class Employee extends Person {
override def toString = super.toString + "..."
}

8.3 Type Checks and Casts

To test whether an object belongs to a given class or its subclass, use isInstanceOf method. If the test succeeds, you can use the asInstanceOf to convert a reference to a subclass reference.

If you want to test whether p refers to a certain object (not its subclass), use if (p.getClass == classOf[Employee]). However, we always use pattern matching for such use cases.