Basic Types
string
println("""|Welcome to Ultamix 3000.
|Type "HELP" for help.""".stripMargin)
Control Structures
for
args.foreach(arg => println(arg))
args.foreach(println)
for (arg <- args) println(arg)
for (i <- 0 to 2) print(greetStrings(i))
for (i <- 1 until 4) println("Iteration "+ i)
// filter, nested for
for (
file <- filesHere
if file.isFile
if file.getName.endsWith(".scala")
) println(file)
for {
file <- filesHere
if file.getName.endsWith(".scala")
line <- fileLines(file)
trimmed = line.trim
if trimmed.matches(pattern)
} println(file +": "+ trimmed)
switch
val friend =
firstArg match {
case"salt" => "pepper"
case"chips" => "salsa"
case "eggs" => "bacon"
case _ => "huh?"
}
exception
try {
val file = new FileReader("input.txt")
} catch {
case ex: FileNotFoundException =>
case ex: IOException =>
} finally {
file.close()
}
Collections
list
val numNames = Array("zero", "one", "two")
val oneTwoThreeFour = oneTwo ::: threeFour
val oneTwoThree = 1 :: 2 :: 3 :: Nil
val empty = Nil
def containsNeg(nums: List[Int]) = nums.exists(_ < 0)
containsNeg(List(1, 2, 3, 4))
def containsOdd(nums: List[Int]) = nums.exists(_ % 2 == 1)
val List(a, b, c) = fruit
// a: String = apples
// b: String = oranges
// c: String = pears
val a :: b :: rest = fruit
// a: String = apples
// b: String = oranges
// rest: List[String] = List(pears)
val abcde = List('a', 'b', 'c', 'd', 'e')
abcde.last // res4: Char = e
abcde.init // res5: List[Char] = List(a, b, c, d)
abcde take 2 // res8: List[Char] = List(a, b)
abcde drop 2 // res9: List[Char] = List(c, d, e)
abcde.indices zip abcde // res17: scala.collection.immutable.IndexedSeq[(Int, Char)] = IndexedSeq((0,a), (1,b), (2,c), (3,d), (4,e))
val zipped = abcde zip List(1, 2, 3) // zipped: List[(Char, Int)] = List((a,1), (b,2), (c,3))
abcde.zipWithIndex // res18: List[(Char, Int)] = List((a,0), (b,1), (c,2), (d,3), (e,4))
List(List(1, 2), List(3), List(), List(4, 5)).flatten // res14: List[Int] = List(1, 2, 3, 4, 5)
List(1, 2, 3, 4, 5) filter (_ % 2 == 0) // List[Int] = List(2, 4)
List(1, 2, 3, 4, 5) partition (_ % 2 == 0) // (List[Int], List[Int]) = (List(2, 4),List(1, 3, 5))
List(1, 2, 3, 4, 5) find (_ % 2 == 0) // Option[Int] = Some(2)
List(1, 2, 3, -4, 5) takeWhile (_ > 0) // List[Int] = List(1, 2, 3)
List(1, 2, 3, -4, 5) dropWhile (_ > 0) // List[Int] = List(-4, 5)
List(1, 2, 3, -4, 5) span (_ > 0) // (List[Int], List[Int]) = (List(1, 2, 3),List(-4, 5))
// A fold left operation “(z /: xs) (op)” involves three objects:
// a start value z, a list xs, and a binary operation op.
// The result of the fold is op applied between successive elements of the list prefixed by z.
// For instance:
// (z /: List(a, b, c)) (op) equals op(op(op(z, a), b), c)
def sum(xs: List[Int]): Int = (0 /: xs) (_ + _)
def product(xs: List[Int]): Int = (1 /: xs) (_ * _)
// The operator has :\ as an analog that produces right-leaning trees. For instance:
// (List(a, b, c) :\ z) (op) equals op(a, op(b, op(c, z)))
def flattenLeft[T](xss: List[List[T]]) = (List[T]() /: xss) (_ ::: _)
List(1, -3, 4, 2, 6) sortWith (_ < _) // List[Int] = List(-3, 1, 2, 4, 6)
List.range(1, 5) // List[Int] = List(1, 2, 3, 4)
List.range(1, 9, 2) // List[Int] = List(1, 3, 5, 7)
List.range(9, 1, -3) // List[Int] = List(9, 6, 3)
List.fill(5)('a') // List[Char] = List(a, a, a, a, a)
List.fill(2, 3)('b') // List[List[Char]] = List(List(b, b, b), List(b, b, b))
List.tabulate(5)(n => n * n) // List[Int] = List(0, 1, 4, 9, 16)
List.concat(List('a', 'b'), List('c')) // List[Char] = List(a, b, c)
List.concat(List(), List('b'), List('c')) // List[Char] = List(b, c)
(List(10, 20), List(3, 4, 5)).zipped.map(_ * _) // List[Int] = List(30, 80)
(List("abc", "de"), List(3, 2)).zipped.forall(_.length == _) // Boolean = true
(List("abc", "de"), List(3, 2)).zipped.exists(_.length != _) // Boolean = false
map
val scores = Map[String, Int]("Abel" -> 10, "Blas" -> 100)
for ((key, value) <- scores)
val cs = TreeSet('f', 'u', 'n')
// cs: scala.collection.immutable.TreeSet[Char] = TreeSet(f, n, u)
var tm = TreeMap(3 -> 'x', 1 -> 'x', 4 -> 'x')
// tm: scala.collection.immutable.TreeMap[Int,Char] = Map(1 -> x, 3 -> x, 4 -> x)
tuple
val tuple = (99, "Luftballons")
println(pair._1)
println(pair._2)
mutable collection
import scala.collection.mutable.Set
val movieSet = Set("Hitch", "Poltergeist")
movieSet += "Shrek"
import scala.collection.mutable.Map
val treasureMap = Map[Int, String]()
treasureMap += (1 -> "Go to island.")
treasureMap += (2 -> "Find big X on ground.")
treasureMap += (3 -> "Dig.")
val romanNumeral = Map(1 -> "I", 2 -> "II", 3 -> "III", 4 -> "IV", 5 -> "V")
Functions
// In Scala operators are not special language syntax: any method can be an operator.
// What makes a method an operator is how you use it. When you write “s.indexOf('o')”,
// indexOf is not an operator. But when you write “s indexOf 'o'”, indexOf is an operator,
// because you’re using it in operator notation.
s indexOf ('o', 5) // Scala invokes s.indexOf(’o’, 5)
// Scala will transform the expression -2.0 into the method invocation “(2.0).unary_-”
// -2.0 can be change to (2.0).unary_-
-2.0
local functions
def processFile(filename: String, width: Int) {
def processLine(filename: String, width: Int, line: String) {
if (line.length > width) println(filename +": "+ line)
}
val source = Source.fromFile(filename)
for (line <- source.getLines()) processLine(filename, width, line)
}
function literals
// A function literal is compiled into a class that when instantiated at run- time is a function value.
var increase = (x: Int) => x + 1
increase(10)
increase = (x: Int) => {
println("We")
x+1
}
short forms of function literals
// One way to make a function literal more brief is to leave off the parameter types.
someNumbers.filter((x) => x > 0)
// A second way to remove useless characters is to leave out parentheses around a parameter whose type is inferred.
someNumbers.filter(x => x > 0)
placeholder
// To make a function literal even more concise, you can use underscores as placeholders for one or more parameters
someNumbers.filter(_ > 0)
// is equivalent to the slightly more verbose x => x > 0
someNumbers.filter(x => x > 0)
// use underscores as placeholders for parameters
val f = (_: Int) + (_: Int) // f: (Int, Int) => Int = <function2>
partially applied functions
// rather than writing println(_), you could write
someNumbers.foreach(println _)
def sum(a: Int, b: Int, c: Int) = a + b + c
val a = sum _ // a: (Int, Int, Int) => Int = <function3>
a(1, 2, 3)
// the Scala compiler translates the expression a(1, 2, 3) into an invocation of
// the function value’s apply method, passing in the three arguments 1, 2, and 3.
// Thus, a(1, 2, 3) is a short form for:
a.apply(1, 2, 3)
val b = sum(1, _: Int, 3) // b: (Int) => Int = <function1>
b(2)
closures
// The function value (the object) that’s created at runtime from this function literal is called a closure.
// such as (x: Int) => x + 1, is called a closed term
var more = 1
val addMore = (x: Int) => /*bound variable*/x + /*free variable*/more
addMore(10) // 11
// what happens if more changes after the closure is created?
// In Scala, the answer is that the closure sees the change.
more = 9999
addMore(10) // Int = 10009
// Intuitively, Scala’s closures capture variables themselves, not the value to which variables refer.
// As the previous example demonstrates, the closure created for (x: Int) => x + more sees the change
// to more made outside the closure. The same is true in the opposite direction.
// Changes made by a closure to a captured variable are visible outside the closure.
val someNumbers = List(-11, -10, -5, 0, 5, 10)
var sum = 0
someNumbers.foreach(sum += _)
sum // Int = -11
special function call forms
// repeated parameters
def echo(args: String*) = for (arg <- args) println(arg)
echo("hello", "world!")
// this notation tells the compiler to pass each element of arr as its own argu- ment to echo,
// rather than all of it as a single argument.
val arr = Array("What's", "up", "doc?")
echo(arr: _*)
function value
def filesMatching(matcher: String => Boolean) =
for (file <- filesHere; if matcher(file.getName))
yield file
def filesEnding(query: String) = filesMatching(_.endsWith(query))
currying
// instead of one list of two Int parameters, you apply this function to two lists of one Int parameter each.
def plainOldSum(x: Int, y: Int) = x + y
def curriedSum(x: Int)(y: Int) = x + y // curriedSum: (x: Int)(y: Int)Int
// what’s happening here is that when you invoke curriedSum, you actually get two traditional function
// invocations back to back. The first function invocation takes a single Int parameter named x,
// and returns a function value for the second function. This second function takes the Int parameter y.
curriedSum(1)(2)
// here’s a function named first that does in spirit what the first traditional function
// invocation of curriedSum would do:
def first(x: Int) = (y: Int) => x + y
val second = first(1)
second(2)
// You can use the placeholder notation to use curriedSum in a partially applied function expression
// The underscore in curriedSum(1)_ is a placeholder for the second parameter list
val onePlus = curriedSum(1)_
onePlus(2)
writing new control structures
def twice(op: Double => Double, x: Double) = op(op(x))
twice(_ + 1, 5) // Double = 7.0
def withPrintWriter(file: File, op: PrintWriter => Unit) {
val writer = new PrintWriter(file)
try {
op(writer)
} finally {
writer.close()
}
}
withPrintWriter(new File("date.txt"), writer => writer.println(new java.util.Date))
// use curly braces instead of parentheses to surround the argument list
def withPrintWriter(file: File)(op: PrintWriter => Unit) {
val writer = new PrintWriter(file)
try {
op(writer)
} finally {
writer.close()
}
}
withPrintWriter(new File("date.txt")) {
writer => writer.println(new java.util.Date)
}
variable arguments
def funcWhichTakesSeq(seq: Any*) = println(seq.length + ": " + seq)
val seq = List(1, 2, 3)
funcWhichTakesSeq(seq) //1: Array(List(1, 2, 3)) -i.e. a Seq with one entry
funcWhichTakesSeq(seq: _*) //3: List(1, 2, 3)
Class
// basic class definition
class ChecksumAccumulator {
private var sum = 0
def add(b: Byte): Unit = sum += b
def checksum(): Int = ~(sum & 0xFF) + 1
}
val acc = new ChecksumAccumulator
// When a singleton object shares the same name with a class, it is called that class’s companion object.
// You must define both the class and its companion object in the same source file.
// The class is called the companion class of the singleton object. A class and its companion object
// can access each other’s private members.
import scala.collection.mutable.Map
object ChecksumAccumulator {
private val cache = Map[String, Int]()
def calculate(s: String): Int =
......
}
// parameters validate, constructor, function operator, toString
class Rational(n: Int, d: Int) {
require(d != 0)
val numer: Int = n
val denom: Int = d
def this(n: Int) = this(n, 1)
def + (that: Rational): Rational = new Rational( numer * that.denom + that.numer * denom, denom * that.denom )
def + (i: Int): Rational = new Rational(numer + i * denom, denom)
override def toString = n +"/"+ d
}
// Implicit conversions.
// The problem here is that 2 * r is equivalent to 2.*(r),
// so it is a method call on the number 2, which is an integer. But the Int class contains no
// multiplication method that takes a Rational argument
val r = new Rational(2,3)
2 * r
// You can create an implicit conversion that automatically converts integers to rational numbers when needed.
// This defines a conversion method from Int to Rational. The implicit modifier in front of the method
// tells the compiler to apply it automatically in a number of situations.
implicit def intToRational(x: Int) = new Rational(x)
// For instance, the Rocket class above can access method fuel, which is declared private in object Rocket.
// Analogously, the Rocket object can access the private method canGoHomeAgain in class Rocket.
class Rocket {
import Rocket.fuel
private def canGoHomeAgain = fuel > 20
}
object Rocket {
private def fuel = 10
def chooseStrategy(rocket: Rocket) {
if (rocket.canGoHomeAgain)
goHome()
else
pickAStar()
}
def goHome() {}
def pickAStar() {}
}
Private constructors and factory methods
// Hiding a primary constructor by making it private.
class Queue[T] private (
private val leading: List[T],
private val trailing: List[T])
// error: constructor Queue cannot be accessed in object $iw new Queue(List(1, 2), List(3))
new Queue(List(1, 2), List(3))
// One possibility is to add an auxiliary constructor
def this() = this(Nil, Nil)
// Another possibility is to add a factory method that builds a queue from such a sequence of initial elements.
object Queue {
// constructs a queue with initial elements ‘xs’
def apply[T](xs: T*) = new Queue[T](xs.toList, Nil)
}
// An alternative: private classes
// hide the class itself and only export a trait that reveals the public interface of the class
trait Queue[T] {
def head: T
def tail: Queue[T]
def enqueue(x: T): Queue[T]
}
object Queue {
def apply[T](xs: T*): Queue[T] = new QueueImpl[T](xs.toList, Nil)
private class QueueImpl[T](
private val leading: List[T],
private val trailing: List[T]) extends Queue[T] {
def mirror = if (leading.isEmpty) new QueueImpl(trailing.reverse, Nil) else this
def head: T = mirror.leading.head
def tail: QueueImpl[T] = {
val q = mirror
new QueueImpl(q.leading.tail, q.trailing)
}
def enqueue(x: T) = new QueueImpl(leading, x :: trailing)
}
}
Traits
// A trait definition looks just like a class definition except that it uses the keyword trait
trait Philosophical {
def philosophize() {
println("I consume memory, therefore I am!")
}
}
// it can be mixed in to a class using either the extends or with keywords
class Frog extends Philosophical {
override def toString = "green"
}
Packages and Imports
Putting code in packages
package bobsrockets.navigation {
class Navigator
}
package bobsrockets {
package navigation {
class Navigator {
// No need to say bobsrockets.navigation.StarMap
val map = new StarMap
}
class StarMap
}
class Ship {
// No need to say bobsrockets.navigation.Navigator
val nav = new navigation.Navigator
}
package fleets {
class Fleet {
// No need to say bobsrockets.Ship
def addShip() { new Ship }
}
}
}
// import selector clause enclosed in braces, which follows the object from which members are imported.
import Fruits.{Apple, Orange}
// the Apple object is renamed to McIntosh
import Fruits.{Apple => McIntosh, Orange}
// This imports all members from object Fruits but renames Apple to McIntosh
import Fruits.{Apple => McIntosh, _}
// This imports all members of Fruits except Pear
import Fruits.{Pear => _, _}
Package objects
// Syntactically, a package ob- ject looks much like one of the curly-braces packagings shown earlier in the chapter.
// The only difference is that it includes the object keyword. It’s a package object, not a package.
package object clay {
println("init package...")
def show = println("package clay")
}
import clay.show
object Demo {
def main(args: Array[String]) {
show
}
}
Case Classes and Pattern Matching
// First, it adds a factory method with the name of the class. This means you can write say,
// Var("x") to construct a Var object instead of the slightly longer new Var("x").
// The second syntactic convenience is that all arguments in the parameter list of a case class implicitly get a val prefix.
// Third, the compiler adds “natural” implementations of methods toString, hashCode, and equals to your class.
// They will print, hash, and compare a whole tree consisting of the class and (recursively) all its arguments.
abstract class Expr
case class Var(name: String) extends Expr
Kinds of patterns
def describe(x: Any) = x match {
case 5 => "five"
case true => "truth"
case "hello" => "hi!"
case Nil => "the empty list"
case _ => "something else"
}
expr match {
case 0 => "zero"
case somethingElse => "not zero: "+ somethingElse
}
expr match {
case BinOp("+", e, Number(0)) => println("a deep match")
case _ =>
}
expr match {
case List(0, _, _) => println("found it")
case _ =>
}
expr match {
case List(0, _*) => println("found it")
case _ =>
}
expr match {
case (a, b, c) => println("Tuple patterns "+ a + b + c)
case _ =>
}
def generalSize(x: Any) = x match {
case s: String => s.length
case m: Map[_, _] => m.size
case _ => -1
}
Pattern guards
// A pattern guard comes after a pattern and starts with an if.
// The guard can be an arbitrary boolean expression, which typically refers to variables in the pattern.
// If a pattern guard is present, the match succeeds only if the guard evaluates to true.
def simplifyAdd(e: Expr) = e match {
case BinOp("+", x, y) if x == y => BinOp("*", x, Number(2))
case _ => e
}
// match only positive integers
case n: Int if 0 < n => ...
// match only strings starting with the letter ‘a’
case s: String if s(0) == 'a' => ...
Sealed classes
// A sealed class cannot have any new subclasses added except the ones in the same file.
// If you match against case classes that inherit from a sealed class,
// the compiler will flag missing combinations of patterns with a warning message.
sealed abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String, left: Expr, right: Expr) extends Expr
// You will get a compiler warning like the following:
// warning: match is not exhaustive!
// missing combination UnOp
// missing combination BinOp
def describe(e: Expr): String = e match {
case Number(_) => "a number"
case Var(_) => "a variable"
}
def describe(e: Expr): String = (e: @unchecked) match {
case Number(_) => "a number"
case Var(_) => "a variable"
}
// pattern for list
def rev[T](xs: List[T]): List[T] = xs match {
case List() => xs
case x :: xs1 => rev(xs1) ::: List(x)
}
Patterns everywhere
val myTuple = (123, "abc")
// number: Int = 123
// string: java.lang.String = abc
val (number, string) = myTuple
val exp = new BinOp("*", Number(5), Number(1))
// op: String = *
// left: Expr = Number(5.0)
// right: Expr = Number(1.0)
val BinOp(op, left, right) = exp
Case sequences as partial functions
val withDefault: Option[Int] => Int = {
case Some(x) => x
case None => 0
}
withDefault(Some(10)) // res28: Int = 10
withDefault(None) // res29: Int = 0
Patterns in for expressions
val results = List(Some("apple"), None, Some("orange"))
for (Some(fruit) <- results) println(fruit)
Stateful Objects
Reassignable variables and properties
// Defining getter and setter methods directly.
class Time {
private[this] var h = 12
private[this] var m = 0
def hour: Int = h
def hour_=(x: Int) {
require(0 <= x && x < 24)
h = x
}
def minute = m
def minute_=(x: Int) {
require(0 <= x && x < 60)
m = x
}
}
// Defining a getter and setter without an associated field.
class Thermometer {
// It is 0 for numeric types, false for booleans, and null for reference types.
// This is the same as if the same variable was defined in Java without an initializer.
var celsius: Float = _
def fahrenheit = celsius * 9 / 5 + 32
def fahrenheit_=(f: Float) {
celsius = (f - 32) * 5 / 9
}
override def toString = fahrenheit + "F/" + celsius + "C"
}
Type Parameterization
Variance annotations
class Variance {
class A
class B extends A
class C extends B
// generic types have by default nonvariant subtyping
class Nonvariant[T] {}
// Prefixing a formal type parameter with a + indicates that subtyping is covariant (flexible) in that parameter.
// By adding this single character, you are telling Scala that you want Queue[String],
// for example, to be considered a subtype of Queue[AnyRef].
// The compiler will check that Queue is defined in a way that this subtyping is sound.
class Covariant[+T] {}
// then if T is a subtype of type S, this would imply that Queue[S] is a sub- type of Queue[T]
class Contravariant[-T] {}
def nonvariant(arg: Nonvariant[B]) = println(arg)
def covariant(arg: Covariant[B]) = println(arg)
def contravariant(arg: Contravariant[B]) = println(arg)
def test {
this.nonvarint(new Nonvariant[B])
// this.nonvariant(new Nonvariant[C]) error
this.covariant(new Covariant[B])
this.covariant(new Covariant[C])
this.contravariant(new Contravariant[B])
this.contravariant(new Contravariant[A])
// this.contravariant(new Contravariant[C]) error
}
}