2. What is Scala?
Scala is a programming language that:
• coherently combines the best aspects of
object-oriented and functional programming
languages,
• runs on the JVM and is fully interopeable with
Java – a .NET version is in the works,
• is statically typed and very concise,
• offers a high level of abstraction and good
performances.
4. Rational numbers
First example: model rational numbers n / d where
n and d are integers, and d ≠ 0.
Provide addition, multiplication and comparison
of rationals.
5. A class for rationals (1)
class Rational(n0: Int, d0: Int) {
require(d0 != 0)
constructor arguments
private def gcd(x: Int, y: Int): Int =
if (y == 0) x else gcd(y, x % y)
no return
no explicit type (Int inferred)
private val g = gcd(n0.abs, d0.abs)
val n: Int = n0 / g public fields
val d: Int = d0 / g (immutable)
def this(n: Int) = this(n, 1)
to be continued…
auxiliary
constructor
6. A class for rationals (2)
…continued
def +(that: Rational): Rational =
new Rational(this.n * that.d +
that.n * this.d,
public
this.d * that.d)
method
def *(that: Rational): Rational =
new Rational(this.n * that.n,
this.d * that.d)
override def toString: String =
n +"/"+ d
}
parameterless
method
7. Using rationals
scala> val r1 = new Rational(1, 3)
r1: Rational = 1/3 primary constructor call
inferred type
scala> val r2 = new Rational(5)
r2: Rational = 5/1
auxiliary constructor call
scala> r1 + r2 // or: r1.+(r2)
res0: Rational = 16/3
scala> res0 * r1 // or: res0.*(r1)
res1: Rational = 16/9
8. A companion for rationals
singleton
companion object for class Rational
object Rational {
def apply(n: Int, d: Int): Rational =
new Rational(n, d)
def apply(n: Int): Rational =
new Rational(n)
val ZERO = new Rational(0)
}
implicitly calls apply
scala> Rational(2,5) + Rational.ZERO
res1: Rational = 2/5
10. Ordered rationals
class Rational(n0: Int, d0: Int)
extends Ordered[Rational] {
… as before
provides <, <=, > and >= methods
def compare(that: Rational): Int =
(n * that.d) compare (that.n * d)
}
11. Cells
Second example: model mutable cells that contain
a single value of some arbitrary type.
Additionally, define logging and undoable
variants.
12. A class for generic cells
make init available as a field
class Cell[T](val init: T) {
private var v = init
mutable field
def get(): T = v
def set(v1: T): Unit = { v = v1 }
≈ Java’s void
override def toString: String =
"Cell("+ v +")"
}
13. A trait for logging cells
trait LoggingCell[T] extends Cell[T] {
override def get(): T = {
println("getting "+ this)
super.get()
}
override def set(v1: T): Unit = {
println("setting "+ this +" to "+ v1)
super.set(v1)
}
}
14. A trait for undoable cells
trait UndoableCell[T] extends Cell[T] {
import collection.mutable.ArrayStack
private var hist = new ArrayStack[T]()
override def set(v1: T): Unit = {
hist.push(super.get())
super.set(v1)
}
def undo(): Unit =
super.set(hist pop)
}
15. Mixin composition
new Cell(0)
basic integer cell
new Cell(0)
logging integer
with LoggingCell[Int]
cell
new Cell(0)
with LoggingCell[Int]
logging, undoable
with UndoableCell[Int] integer cell (undos are
logged)
new Cell(0)
with UndoableCell[Int]
with LoggingCell[Int]
logging, undoable
integer cell (undos are
not logged)
17. Optional values
An optional value is either empty, or contains a
single element.
Example use: as a clean replacement for null.
variance (here co-variant)
abstract class Option[+T] {
def isEmpty: Boolean
def get: T
bounded type parameter
def getOrElse[U >: T](d: =>U): U =
if (isEmpty) d else get by-name parameter
…many more methods
}
18. Optional values (2)
can be matched (see later) & compiler-provided methods
case class Some[+T](x: T)
extends Option[T] {
def isEmpty = false
def get = x
}
case object None subtype of all types
extends Option[Nothing] {
def isEmpty = true
def get = throw new …
}
20. Tuples
A tuple of size n contains exactly n heterogenous
elements – i.e. they can be of different types.
Example use: a function that has to return n values
can return them wrapped in a single tuple.
case class Tuple2[+T1,+T2](
_1: T1, _2: T2)
case class Tuple3[+T1,+T2,+T3](
_1: T1, _2: T2, _3: T3)
etc.
21. Short tuple syntax
Scala offers syntactic shortcuts for tuple values:
(e1, …, en) ≣ Tuplen(e1, …, en)
and for tuple types:
(T1, …, Tn) ≣ Tuplen[T1, …, Tn]
Example:
scala> val p = ("Orwell", 1984)
p: (String, Int) = (Orwell,1984)
scala> p._1 arguments of case classes are fields
res1: String = Orwell
22. (Im)mutable collections
Options and tuples are immutable.
Standard collections (sequences, sets, maps) are
provided in mutable and immutable variants.
Mutable collections are similar to the ones found
in Java and other imperative languages.
Immutable collections are similar to the ones
found in typical functional languages.
23. Immutable lists
An immutable list is either empty or composed of
a head element and a tail, which is another list.
abstract class List[+T] {
def isEmpty: Boolean
def head: T
def tail: List[T]
def ::[U >: T](x: U): List[U] =
new ::[U](x, this)
…many more methods
}
24. Immutable lists (2)
case class ::[T](val head: T,
val tail: List[T])
extends List[T] {
def isEmpty = false
}
case object Nil extends List[Nothing] {
def isEmpty = true
def head: Nothing =
throw new …
def tail: List[Nothing] =
throw new …
}
25. Sequences, maps and sets
scala> val aSeq = Seq("zero","one","two")
scala> aSeq(1)
res1: String = one
scala> val aMap = Map("zero" -> 0,
"one" -> 1,
"two" -> 2)
scala> aMap("one")
res2: Int = 1
scala> val aSet = Set("zero","one","two")
scala> aSet("one")
res3: Boolean = true
26. For loops
For loops enable iteration over the elements of
collections, using a syntax that is reminiscent of
SQL queries.
for (user <- users
if (user.isMale
&& user.age > 30))
yield user.name
28. Pattern matching
Instances of case classes can easily be
constructed:
scala> Some((1,2))
res: Some[(Int,Int)] = Some((1,2))
Pattern matching makes deconstruction of case
classes similarly easy.
29. Integer division
def divMod(n: Int, d: Int):
Option[(Int,Int)] = {
if (d == 0)
None
else {
…compute quotient q and remainder r
Some((q, r))
}
}
31. Pattern matching lists
Since lists are recursive, it is natural to manipulate
them with recursive functions.
Furthermore, since they are defined as a
disjunction of two cases (non-empty / empty list),
it is natural to use pattern matching to do a case
analysis.
Example: a function to sum a list of integers.
def sum(l: List[Int]): Int = l match {
case h :: t => h + sum(t)
case Nil => 0
}
32. Typed patterns
Pattern matching can also be used to discriminate
on the type of a value:
supertype of all types
def succAny(x: Any): Any = x match {
case i: Int => i + 1 Int and Long are not
case l: Long => l + 1
case classes
case other => other
}
scala> succAny(2)
res1: Any = 3
scala> succAny("two")
res2: Any = two
33. Exception handling
Unsurprisingly, exception handling is done using
pattern matching:
try {
val f = new FileInputStream("f.txt")
println(f.read())
f.close()
} catch { _ is a wildcard
case _: FileNotFoundException =>
println("not found")
case e: IOException =>
println("other error: " + e)
}
35. What is FP?
The functional style of programming is one that
relies on mathematical functions as the basic
building block of programs.
In mathematics, a function always returns the
same result when applied to the same arguments.
Therefore, the functional style of programming
strongly discourages the use of side-effects – i.e.
variables and other kinds of mutable data.
36. What is a FPL?
A functional programming language is one that
encourages the functional style of programming
by:
• offering first-class functions that can be
manipulated like any other value,
• providing lightweight syntax to define
arbitrarily-nested functions,
• discouraging the use of side-effects, for
example by providing libraries of immutable
data-structures.
37. Why is FP interesting?
Side-effects complicate several classes of
programs, like:
• concurrent programs with shared, mutable
state,
• programs that need to do “time travel”, e.g.
interactive programs with undo, SCMs, …
• programs that need to work on a consistent
state of some data, e.g. a live backup program,
• etc.
38. A trait for functions
trait Function1[F, T] { f =>
def apply(x: F): T
new name for this
def compose[F2](g: Function1[F2, F])
: Function1[F2, T] =
new Function1[F2, T] {
def apply(x: F2): T =
f.apply(g.apply(x))
}
override def toString = "<fun>"
}
39. Using functions as values
scala> val succ = new Fun[Int,Int] {
def apply(x: Int) = x + 1 }
succ: …with Function1[Int,Int] = <fun>
scala> val twice = new Fun[Int,Int] {
def apply(x: Int) = x + x }
twice: …with Function1[Int,Int] =<fun>
scala> succ.apply(6)
res1: Int = 7
scala> twice.apply(5)
res2: Int = 10
scala> (succ compose twice).apply(5)
res3: Int = 11
40. se
!
Using functions as values
W
or
ks
,b
ut
w
ay
to
o
ve
rb
o
scala> val succ = new Fun[Int,Int] {
def apply(x: Int) = x + 1 }
succ: …with Function1[Int,Int] = <fun>
scala> val twice = new Fun[Int,Int] {
def apply(x: Int) = x + x }
twice: …with Function1[Int,Int] =<fun>
scala> succ.apply(6)
res1: Int = 7
scala> twice.apply(5)
res2: Int = 10
scala> (succ compose twice).apply(5)
res3: Int = 11
41. Functional syntactic sugar
anonymous function
scala> val succ = { x: Int => x + 1 }
succ: (Int) => Int = <function1>
a.k.a. Function1[Int, Int]
scala> val twice = { x: Int => x + x }
twice: (Int) => Int = <function1>
scala> (succ compose twice)(5)
res1: Int = 11
implicit apply
42. Partial application
Function values can also be created by applying
existing functions or methods to a (possibly empty)
subset of their arguments:
scala> val succ: Int=>Int = _ + 1
succ: (Int) => Int = <function1>
scala> val toStr: Any=>String = _.toString
toStr: (Any) => String = <function1>
scala> toStr(0123)
res0: String = 83
43. Collections as functions
Most collections are functions:
• Seq[T] has type Int=>T,
• Map[K,V] has type K=>V,
• Set[T] has type T=>Boolean.
This explains the common notation to access their
elements: it’s simply function application!
44. Operating on collections
Functional values are ideal to operate on
collections. Examples:
scala> val s = Seq(1,2,3,4,5)
scala> s map (_ + 1)
res1: Seq[Int] = List(2, 3, 4, 5, 6)
scala> s reduceLeft (_ * _)
res2: Int = 120
scala> s filter (_ % 2 == 0)
res3: Seq[Int] = List(2, 4)
scala> s count (_ > 3)
res4: Int = 2
scala> s forall (_ < 10)
res5: Boolean = true
46. Solving problem 8
val n = "7316717653133062491922511967442…"
val digits = s.toList map (_.asDigit)
def prods(l: List[Int]): List[Int] =
l match {
case a :: (t@(b :: c :: d :: e :: _)) =>
(a * b * c * d * e) :: prods(t)
case _ =>
List()
}
prods(digits) reduceLeft (_ max _)
47. For loops translation
The for notation is pure syntactic sugar. The
example:
for (user <- users
if (user.isMale
&& user.age > 30))
yield user.name
is automatically expanded to:
users
.filter(user => user.isMale
&& user.age > 30)
.map(user => user.name)
49. Scala implicits
Scala offers two notions of implicit entities that are
automatically inserted by the compiler in certain
contexts:
• Implicit conversions, which can be applied
automatically to transform a type-incorrect
expression into a type-correct one.
• Implicit parameters, which can be
automatically passed to a function.
50. Implicit conversions
An implicit conversion from Int to Rational
can be added to the latter’s companion object:
object Rational {
…as before
implicit def i2r(i: Int): Rational =
Rational(i)
}
scala> 2 + Rational(1,3) // i2r(2) + …
res2: Rational = 7/3
scala> Rational(1,3) + 3 // … + i2r(3)
res3: Rational = 10/3
51. “Pimp my library”
Implicit conversions make it possible to
“augment” existing classes by implicitly wrapping
them – known as the Pimp my library pattern.
This technique is heavily used in the standard
library to improve anemic Java classes (e.g.
String, Integer, Boolean, arrays, etc.).
scala> "1337" strings are really Java strings
res1: java.lang.String = 1337
implicit conversion
scala> "1337".toInt
res2: Int = 1337
52. Implicit parameters
repeated parameter
def max[T](xs: T*)
(implicit ord: Ordering[T]): T =
xs reduceLeft ord.max
implicitly Ordering.Int
scala> max(4, -2, 12, 25, 7, -1, 2)
res1: Int = 25
scala> max(4, -2, 12, 25, 7, -1, 2)
(Ordering.Int.reverse)
res2: Int = -2
scala> max(1 to 20 : _*)
res3: Int = 20
pass all elements as
separate arguments
54. XML literals
Scala supports XML literals, which are translated
to instances of various classes in scala.xml.
scala> val hello =
<p>Hello <b>world</b></p>
hello: scala.xml.Elem =
<p>Hello <b>world</b></p>
scala> val name = "Roger&Co."
name: java.lang.String = Roger&Co.
scala> val hello = arbitrary Scala expression
<p>Hello <b>{ name }</b></p>
hello: scala.xml.Elem =
<p>Hello <b>Roger&Co.</b></p>
58. Properties for rationals
The operations we defined on rationals should
satisfy several properties:
• ∀x, y ∈ Q: x + 0 = 0 + x = x
• ∀x, y ∈ Q: x + (y + z) = (x + y) + z
• etc.
ScalaCheck makes it possible to express such
properties and test them on random rationals.
Extensive use of implicits keep the client code
very concise.
59. Properties for rationals
object RatProps extends Properties("Rat") {
property("+ left unit") =
Prop.forAll((x: Rational) => 0 + x == x)
property("+ right unit") =
Prop.forAll((x: Rational) => x + 0 == x)
property("+ associativity") =
Prop.forAll((x: Rational,
y: Rational,
z: Rational) =>
(x + y) + z == x + (y + z))
}
60. Generating rationals
ScalaCheck doesn’t know how to generate
arbitrary rationals, but we can easily define a
generator for them:
implicit def arbRat: Arbitrary[Rational] =
Arbitrary {
Gen.sized(sz =>
for (n <- Gen.choose(-sz, sz);
d <- Gen.choose(-sz, sz)
suchThat (_ != 0))
yield Rational(n, d))
}
61. Testing rationals
%
+
+
+
scala
Rat.+
Rat.+
Rat.+
RatProps
left unit: OK, passed 100 tests.
right unit: OK, passed 100 tests.
associativity: OK, passed 100 tests.
When a property can be falsified (here the incorrect
property ∀x ∈ Q: x = 0), the counter-example is
presented:
! Rat.wrong: Falsified after 0 passed tests.
> ARG_0: 1/2
62. Further reading
http://www.scala-lang.org/
Two books about Scala:
• Programming in Scala by Odersky, Spoon &
Venners
• Programming Scala by Wampler & Payne
One book about functional programming:
• The Functional Approach to Programming by
Cousineau, Mauny & Callaway