The left panel shows the functional features, the main one describes the type system, and the right the object oriented parts.
REPL:
sbt console
(from Scala Build Tools)scala
IDE:
Other:
make
)Style guide (please use):
What is Scala’s Unified Object Model?
Basic types:
Boolean
Char
, Int
, Long
, Short
, Byte
Double
, Float
String
Symbol
Nothing
, Null
, Unit
Any
, AnyVal
, AnyRef
Functions:
Int => Int
Int => Int => Int
Int => Unit
Tupples:
(Int, Int)
What is the reson for introducing type Unit?
Unit:
val u: Unit = ()
Null:
val n: Null = null
val s: String = null
Aliasing:
type Identifier = Int
type Callback = Int => Int
type Vector3D = (Int, Int, Int)
Symbols:
val x = 'A
val y = 'B
Arithmetic:
+ -
/ % *
Relational and logical:
< > <= >= == !=
|| && !
Bit-wise:
& | ^ ~
>> << >>>
Assignment:
= *= += -= /=
And more: full list of operators
val x = 5
val s = "spaceship"
val y : Int = 0
Evaluated at definition. Cannot redefine.
x = x + 1
<console>:4: error: reassignment to val
var x = 5
var s = "spaceship"
Evaluated at definition. Can redefine.
x = x + 1
s = "whoosh"
Default value of variable:
var x: Int = _
var y: Boolean = _
var s: String = _
var z: Any = _
def f = 1
def g = 1 + f
def h = (x: Int) => 1 + x
def h: Int => Int = x => 1 + x
Evaluated on use (Every time). Can redefine.
Limits of type inference:
def h = x => 1 + x
<console>:7: error: missing parameter type
def h = (x) => 1 + x
When and why to use val
, var
, def
?
Everything is an object:
5.toString()
Operators are instance methods:
val x, y = 2
x + y
x.+(y)
Removing parentheses from method calls:
val x = 2
x.toString()
x.toString // <==
x toString
Style guide: arity 0, pure methods only (no side effects).
Infix notation:
val s = "something something spaceship"
s.split(" ")
s split(" ")
s split " " // <==
Style guide: arity 1, pure methods only (no side effects). Use especially if the argument is a function.
Definition:
{ /*...*/ }
A block returns the last evaluated value:
{ 2 + 2 }
{ val x = 2; x }
{ val x = 2; val y = 2; x + y }
{ println("x") }
Variable scope and blocks:
val x = { 2 + 2 } // x → 4
val y = { x + 2 } // y → 6
{ val xx = 8; xx } // x → 8
xx // explosion
{ x; val x = 8 } // explosion
Method definition (in the current object).
def increment(x: Int): Int = {
return x + 1
}
def increment(x: Int): Int = {
x + 1
}
def increment(x: Int) = {
x + 1
}
def increment(x: Int) {x + 1} // → unit
def increment(x: Int) = x + 1
Method nesting:
def outer(x: Int) = {
def inner(y: Int) = y + x
inner(x)
}
Unit argument:
def f() = 5
def g() = ()
Parameter with default values:
def add(x: Int, y: Int = 1) = x + y
add(1, 1)
add(1)
Multiple parameter groups and partial application:
var debugLevel = 5
def debug(level: Int)(message: String) { if (level >= debugLevel) println(message) }
debug(5)("a thing is happening")
val warn = debug(0) _
val error = debug(10) _
warn("a meh thing is happening")
error("a bad thing is happening")
var debugLevel = 5
def debug(level: Int)(message: => String) { if (level >= debugLevel) println(message) }
warn("a" + " meh " + "thing is happening")
Function (lambda) definitions:
(x: Int) => x + 1
() => println("Hello World!")
val f1 = (x: Int) => x + 1
f1(5)
val f2 = () => println("Hello World!")
f2()
Functions are anonymous.
Functions are objects:
val f1 = (x: Int) => x + 1
f1.apply(1)
f1.toString
Explicit method to function conversion:
def increment(x: Int) = {x + 1}
val f = increment _ // η-conversion aka eta-conversion aka magic
val fs = "s".toString _
fs()
Automatic conversion:
def increment(x: Int) = {x + 1}
increment.apply(1)
increment.toString
val x = increment
x(1)
x.apply(1)
x.toString()
Watch out when converting arrity 0 methods.
val f = (x: Int) => x * x
val g = (x: Int) => x + 1
f(g(1)) // (x + 1) * (x + 1) = 4
(f compose g)(1) // (x + 1) * (x + 1) = 4
g(f(1)) // (x * x) + 1 = 2
(f andThen g)(1) // (x * x) + 1 = 2
val sum = (a: Int, b: Int, c: Int) => a + b + c
val f1 = sum(1, 2, _: Int)
val f2 = sum(1, _: Int, _: Int)
f1(1)
f2(1, 1)
val typeOf: Any => String = {
case x: Int => "Int"
case x: Double => "Double"
}
typeOf(1)
typeOf(1.0)
typeOf("spaceship")
scala.MatchError: spaceship (of class java.lang.String)
at $anonfun$1.apply(<console>:7)
at $anonfun$1.apply(<console>:7)
... 33 elided
What are first-order functions?
What are higher-order functions?
First class functions can be treated as values: assigned to variables, passed by arguments.
val a = (x: Int) => x + 1
val b = a
Higher order function can accept functions as arguments and/or return functions.
val f = (x: Int) => (y: Int) => x + y
val g = (x: Int) => { val z = x + 1; (y: Int) => z + y }
val h = (fun: Int => Int) => fun(1)
Minimal definition:
class Enterprise() {}
Default constructor (creates fields, executes code):
class Enterprise() { // primary constructor
val version = "NCC-1701"
val captain = "Kirk, J. T."
val maxShields = 100
var shieldDmg = 0
def shieldLvl() = {maxShields - shieldDmg}
println("Split all the infinitives!")
}
Creating objects:
val enterprise = new Enterprise()
private
- private (visible to class)private[package]
- private (visible to package)private[this]
- private (visible to instance)protected
- protected (visible to class and subclasses)protected[package]
- protected (visible to package)protected[this]
- protected (visible to instance)public
Default visibility is public
.
private
vs private[this]
Access in any instance of the class with private
class Spaceship {
private val name = "USS Enterprise"
def compare (e: Spaceship): Boolean = e.name == this.name
}
Access only within instance:
class Spaceship {
private[this] val name = "USS Enterprise"
def getName() = name
def compare (e: Spaceship): Boolean = e.getName() == this.name
}
class Enterprise(ver: String, capt: String) {
val version = ver
val captain = capt
val maxShields = 100
var shieldDmg = 0
def shieldLvl() = {maxShields - shieldDmg}
}
val enterprise = new Enterprise("NCC-1701", "Kirk, J. T.")
enterprise.shieldDmg = 10
enterprise.shieldLvl()
enterprise.shieldLvl
class Enterprise(ver: String, capt: String) {
def version = ver
def captain = capt
val maxShields = 100
var shieldDmg = 0
def shieldLvl = {maxShields - shieldDmg}
}
val enterprise = new Enterprise("NCC-1701", "Kirk, J. T.")
enterprise.shieldDmg = 10
enterprise.shieldLvl
class Enterprise(ver: String, capt: String) {
def this() = {
this("NCC-1701", "Kirk, J. T.") // First expression in method
}
}
abstract class Constitution() { // extends AnyRef
def shipClass = "Constitution" // or val
def shipName = {}
}
class Enterprise(ver: String, capt: String) extends Constitution {
override def shipName = "Enterprise"
}
abstract class Constitution() { // extends AnyRef
var shipClass = "Constitution"
def shipName = {}
}
class Enterprise(ver: String, capt: String) extends Constitution {
override var shipClass = "Enterprise"
}
<console>:9: error: overriding variable shipClass in class Constitution of type java.lang.String;
variable shipClass cannot override a mutable variable
override var shipClass = "Enterprise"
^
trait WarpSpeedCapability {
var currentSpeed = 0
def setCruisingSpeed = { currentSpeed = 2 }
def setMaximumWarp = { currentSpeed = 9 }
}
class Enterprise(ver: String, capt: String) extends Constitution with WarpSpeedCapability {
override def shipName = "Enterprise"
}
Solving conflicts:
trait AuxiliaryCraft {
val awayTeamType = "shuttlecraft"
def sendAwayTeam = {
// ...
}
}
trait Transporter {
val awayTeamType = "transporter"
def sendAwayTeam = {
// ...
}
}
class Enterprise(ver: String, capt: String) extends AuxiliaryCraft with Transporter {
override def awayTeamType = "transporter or shuttlecraft"
override def sendAwayTeam = {
// ...
}
}
Unreconcileable conflict:
trait AuxiliaryCraft {
var isAwayTeamSent = false
def sendAwayTeam = {
// ...
}
}
trait Transporter {
var isAwayTeamSent = false
def sendAwayTeam = {
// ...
}
}
class Enterprise(ver: String, capt: String, maxShld: Int) extends AuxiliaryCraft with Transporter {
override def sendAwayTeam = {
// ...
}
}
<console>:9: error: class Enterprise inherits conflicting members:
variable isAwayTeamSent in class AuxiliaryCraft$class of type Boolean and
variable isAwayTeamSent in class Transporter$class of type Boolean
(Note: this can be resolved by declaring an override in class Enterprise.);
other members with override errors are: isAwayTeamSent_=
What restrictions are placed on method identifiers in Scala?
class C {
def < (that: Any): Boolean = {
// ...
}
}
Static object definition (singleton):
object universe {
val age: Long = 45L
def getBlackHoles() = {
// ...
}
}
Scala does not allow creating static elements outside of static objects.
Companion object (pattern):
class Shipyard {
def makeShip = ???
}
object Shipyard {
def makeShipyard = ???
def getAllShipyards = ???
}
When are static objects created?
Defining main
functions:
object Something {
def main(args: Array[String]) {
// Code goes here.
}
}
object Something extends App {
// Code goes here.
}
class RefCell[T] {
private var obj: T = _
def get: T = { obj }
def set(value: T) = { obj = value }
}
val s = new RefCell[String]
s set "HelloWorld"
s get
val x = new RefCell[Int]
x set 5
x get
Immutable: * tuples * lists * sets * maps * strings
Mutable:
val pair = ("alpha", "beta")
val triple = ("alpha", "beta", "gamma")
val x = pair _1
val y = pair _2
val (x,y) = pair
val riap = pair swap
val pair = "alpha" -> "beta"
val c = List(1, 2, 3, 4)
val c = 1 :: 2 :: 3 :: 4 :: Nil
val c = List(1, 2) ::: List(3, 4)
c(0)
c head
c tail
c last
c isEmpty
c length
c reverse
c iterator
c slice (0, 2)
val s = Set(1, 2, 3, 4, 4)
s(1)
s contains 1
s - 1
s + 5
s + (6,7)
s empty
val a = Set(1, 2, 3, 4)
val b = Set(3, 4, 5, 6)
a union b // union
a | b // union
a ++ b // union
a subsetOf b
a intersect b // intersection
a & b // intersection
a diff b // difference
a &~ b // difference
a -- b // difference
val r = Range(0, 10)
r(0)
val r = 1.to(10)
val r = 1.until(10)
val r = 1 to 10
val r = 1 until 10
r reverse
r iterator
val m = Map("a" -> 1, "b" -> 2)
val m = Map(("a", 1), ("b", 2))
m("a")
m get "a"
m get "x"
m getOrElse ("x", -1)
m contains "a"
m + ("c" -> 3)
m + ("c" -> 3, "d" -> 4)
m ++ Map("c" -> 3, "d" -> 4)
m - "a"
m - ("a", "b")
m -- Map("c" -> 3, "d" -> 4)
m keys
m values
val arr = new Array[String](3)
arr(0) = "ala"
arr(1) = "ma"
arr(2) = "kota"
val arr = Array("ala", "ma", "kota")
val cube = ofDim[Int](3,3)
cube(0,0) = 1
import scala.collection.mutable.LinkedList
import scala.collection.mutable.Set
import scala.collection.mutable.Map
import scala.collection.mutable.StringBuilder
val s : StringBuilder = new StringBuilder()
s append "a"
s ++= "b"
println(s.mkString)
val ls : List[String] = "a" :: "b" :: "c" :: Nil
Covariance: If A is a subtype of B then T[A] is a subtype of T[B].
class Animal {}
class Dog extends Animal {}
val animals : List[Animal] = new Dog() :: new Dog() :: Nil
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1
val l = List(1, 1, 1 ,1 ,1)
l.fold(0)((acc: Int, elem: Int) => acc + elem)
val ll = List(List(1, 1, 1), List(2, 2, 2))
ll.fold(List[Int]())((acc: List[Int], elem: List[Int]) => acc ::: elem)
Specified folding order:
def foldLeft[B](z: B)(op: (B, A) => B): B
def foldRight[B](z: B)(f: (A, B) => B): B
val l = List(1, 2, 3 ,4 ,5)
l.foldLeft(0)((acc: Int, elem: Int) => {println(acc+"+"+elem); acc + elem})
l.foldRight(0)((elem: Int, acc: Int) => {println(acc+"+"+elem); acc + elem})
l.foldLeft(List[Int]())((acc: List[Int], elem: Int) => elem :: acc)
l.foldRight(List[Int]())((elem: Int, acc: List[Int]) => elem :: acc)
val l = List(1, 2, 3 ,4 ,5)
l reduce { (acc, elem) => acc + elem }
l reduceLeft { (acc, elem) => acc + elem }
l reduceRight { (elem, acc) => acc + elem }
val l = List(1, 2, 3 ,4 ,5)
l map { x => x + 1 }
l map { x => x.toString }
val l = List(0, 1, 2, 3 ,4 ,5)
l flatMap { x => if (x == 0) List(x) else List(-x, x) }
l flatMap { x => if (x % 2 == 0) List(x) else List() }
val l = List(1, 2, 3, 4, 5)
val even = l filter { x => (x % 2) == 0 }
val odd = l filterNot { x => x % 2 == 0 }
val l = List(1, 2, 3, 4, 5)
val (even, odd) = l partition { x => x % 2 == 0 }
val l = List(1, 2, 3, 4, 5, 1, 2)
l groupBy { x => x % 2 }
l groupBy { x => x }
("to boldly go where no man has gone before" split " ") groupBy { x => x.charAt(0) }
if (x > 0)
z = x
else
z = -x
while (x > 0)
x -= 1
while (x > 0) {
x -= 1
println(x)
}
For loops:
l = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
for (i <- l)
println(i)
for (i <- 1 to 10)
println(i)
for (i <- 1 until 10)
println(i)
for (i <- 1 to 10; j <- 1 to 10)
println(i, j)
Loops with filters:
for (i <- 1 until 10 if i % 2 == 0)
println(i)
for (i <- 1 until 10 if i % 2 == 0; if i % 3 == 0)
println(i)
Lazy loop execution:
val result = for (i <- 1 to 10 if i % 2 == 0) yield i
result(3)
Matching by value:
def f(x: Any) = {
x match {
case "1" => 1
case "a" | "A" => 10
case "b" | "B" => 11
case _ => -1
}
}
Matching by type:
def f(x: Any) = {
x match {
case i: Int => i
case s: String => {
val lcase = s toLowerCase
val ch = (lcase toCharArray) apply 0
ch - 96
}
case _ => -1
}
}
Matching collections:
def sum (l: Any, r: Int = 0) = {
l match {
case Nil => r
case x :: tail => sum(tail, r + x)
}
}
def fuzzyLength (l: Any) = {
l match {
case Nil => "empty"
case _ :: Nil => "one"
case _ :: _ :: Nil => "two"
case _ :: _ :: tail => "more than two"
case _ => "probably not a list"
}
}
Conditional matching:
def grade(avg: Double) : String = {
avg match {
case x if x >= 90.0 => "A"
case x if x >= 85.0 => "A-"
case x if x >= 80.0 => "B+"
case x if x >= 75.0 => "B"
case x if x >= 70.0 => "B-"
case x if x >= 65.0 => "C"
case x if x >= 55.0 => "D"
case x if x >= 0.00 => "E"
case _ => "Um... I mean... in a way, I'm impressed."
}
}
Case classes:
abstract class BTreeNode
case class Leaf(v: Int) extends BTreeNode
case class ConcreteNode(l: Leaf, r: Leaf, v: Int) extends BTreeNode
def sum(node: BTreeNode): Int = {
node match {
case ConcreteNode(l, r, v) => v + sum(l) + sum(r)
case Leaf(v) => v
}
}
Option types:
def f(x: Option[String]) = {
x match {
case None => throw new Exception()
case Some(s) => s.split(" ")
}
}
package pl.edu.put.Test
package pl {
package edu {
package put {
package Test {
// ...
}
}
}
}
import scala.math._
val pair = (2, 3)
val (x, _) = pair
1::2::3::Nil map (e => -e)
1::2::3::Nil map (-_)
Threads:
val th = new Thread(new Runnable {
def run() {
println("new thread")
}
})
th start
println("old thread")
th join
Example, function running in separate thread:
def start[T](expression: => T) = {
val th = new Thread(new Runnable{ def run() { expression } })
th.start; th
}
start((0 until 100) map {e => println(e * e)})
Concurrent sequence:
var seq = 0
class Incr extends Thread {
override def run () {
println(seq)
seq = seq + 1
}
}
(0 until 10) map ((_) => (new Incr).start)
Critical section synchronization:
var seq = 0
class Incr extends Thread {
override def run () {
(this getClass) synchronized {
println(seq)
seq = seq + 1
}
}
}
(0 until 10) map ((_) => (new Incr).start)
A volatile variable:
@volatile var seq = 0
Synchronization primitives from java.util.concurrent._
:
import java.util.concurrent.atomic.AtomicInteger
val aint = new AtomicInteger(0)
aint set 1
aint get
import java.util.concurrent.locks.ReentrantReadWriteLock
val l = new ReentrantReadWriteLock
val rl = l readLock
val wl = l writeLock
rl lock
rl unlock
ExecutorService
ForkJoinPool
Thread pool:
import java.util.concurrent.Executors
val cores = Runtime.getRuntime.availableProcessors
val pool = Executors.newFixedThreadPool(cores)
def toRunnable[T] (expression: => T) = { new Runnable{ def run() { expression } } }
pool.execute{ toRunnable { Thread sleep 1000; println("Hello") } }
pool.execute{ toRunnable { Thread sleep 10; println("World") } }
pool.shutdown
Thread pool with results and Java-Scala conversion.
def toCallable[T] (expression: => T) = {
new java.util.concurrent.Callable[T]{ def call() = { expression } }
}
val tasks = ((0 until 100) toList) map (e => toCallable {e * e})
import scala.collection.JavaConversions._
val jTasks = asJavaCollection(tasks)
val jResult = pool.invokeAll( jTasks )
val result = (asScalaBuffer(jResult) toList) map (e => e.get)
Parallel collections:
val list = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9).par
list foreach { e => println(e) }
(list.par) foreach { e => println(e) }
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
val f: Future[List[Int]] = Future { ((0 until 100) toList) map (e => e * e) }
val f: Future[List[Int]] = Future { Thread sleep 1000; ((0 until 100) toList) map (e => e * e) }
Asynchronous reading from futures.
f onSuccess { case result => println("done!" + result.last) }
f onFailure { case exception => println("fail!" + exception) }
Synchronous reading from futures.
f value
import scala.concurrent.Await
import scala.concurrent.duration._
Await.result(f, 100 seconds)
Await.result(f, Duration.Inf)
Folding: Future.reduce
, Future.fold
.
Example of using Futures for concurrency:
val l = (0 until 100) toList
val f = (x:Int) => println(x)
l map { e => Future { f(e) } }
val f = (x:Int) => { println(x); x * x }
l map { e => Future { f(e) } } map { e => Await.result(e, Duration.Inf) }