Scala @ BeJUG
Luc DuponcheelImagineJ
September 2, 2009
Introduction
Chapter 0
Introduction
Types
I Scala is a (taste)fully typed language
Types
I Scala supports generic types
Expressions
I Scala is an expression oriented language
Objects
I Scala is an pure object oriented language
Functions
I Scala is a functional language
Scalable language
I Scala is a scalable languageI Scala does not grow on a language basisI Scala grows on a library basis
Usage
Chapter 1
Usage
Expression evaluation: Double
scala> 1.2.*(2.3)
res0: Double = 2.76
scala> 1.2 * 2.3
res1: Double = 2.76
Expression evaluation: String
scala> "Hello world".substring(2,9)
res2: java.lang.String = llo wor
scala> "Hello world" substring (2,9)
res3: java.lang.String = llo wor
Function result
def plus(x: Int) = (y: Int) => y + x
Function result
scala> :l plus.scala
Loading plus.scala...
plus: (x: Int)(Int) => Int
scala> plus(1)
res0: (Int) => Int = <function1>
Apply plus(1)
scala> plus(1).apply(1)
res1: Int = 2
scala> plus(1)(1)
res2: Int = 2
Function parameter
def applyToZero[X](f: Int => X) = f(0)
Function parameter
scala> :l applyToZero.scala
Loading applyToZero.scala...
applyToZero: [X]((Int) => X)X
scala> applyToZero (x => x + 1)
res0: Int = 1
Function parameter and result
def twice[X](f: X => X)
= (x: X) => f(f(x))
Function parameter and result
scala> :l twice.scala
Loading twice.scala...
twice: [X]((X) => X)(X) => X
scala> twice((x: Int) => x + 1)(0)
res0: Int = 2
Many parameter lists
def twice[X](f: X => X)(x: X) = f(f(x))
Function parameter and result
scala> :l twiceMany.scala
Loading twiceMany.scala...
twice: [X](f: (X) => X)(x: X)X
scala> twice((x: Int) => x + 1)(0)
res0: Int = 2
Placeholder syntax
scala> :l applyToZero.scala
Loading applyToZero.scala...
applyToZero: [X]((Int) => X)X
scala> applyToZero (_ + 1)
res0: Int = 1
Patterns
Chapter 2
Patterns
Compositional reuse
I Compositional reuse is reuse of
I code fragmentsthe building blocks, starting from atomic ones
I code templatesthe containers into which the building blockscan be plugged
Natural numbers and digits
type Nat = Int
type Dig = Int
Structural recursion
def structRec[A]
(a: A, op: (Dig, A) => A) : Nat => A = {
(n: Nat) =>
if(n == 0) {
a
} else {
op(n % 10, structRec(a, op)(n / 10))
}
}
Using structural recursion
def sumOfDigits =
structRec[Int](0, _ + _)
def hasDigit(d: Dig) =
structRec[Boolean](false, _ == d || _)
def exists(p: Dig => Boolean) =
structRec[Boolean](false, p(_) || _)
Using structural recursion
scala> sumOfDigits(1234)
res0: Int = 10
scala> hasDigit(2)(1234)
res1: Boolean = true
scala> exists(_ == 2)(1234)
res2: Boolean = true
unfoldAndThenFold
6
---- | / \ |
| dig = 3 + 3 = rec |
-----------------------
| 2 + 1 |
| / \ |
| 1 + 0 |
| / \ |
| 0 0 |
List of digits
:
/ \
3 :
/ \
2 :
/ \
1 :
/
0
Sum of list of digits
+
/ \
3 +
/ \
2 +
/ \
1 +
/ \
0 0
Tail recursion
def tailRec[A]
(a: A, op: (Dig, A) => A) : Nat => A = {
(n: Nat) =>
if(n == 0) {
a
} else {
tailRec(op(n % 10, a), op)(n / 10)
}
}
Using tail recursion
def sumOfDigits =
tailRec[Int](0, _ + _)
def hasDigit(d: Dig) =
tailRec[Boolean](false, _ == d || _)
def exists(p: Dig => Boolean) =
tailRec[Boolean](false, p(_) || _)
Using tail recursion
scala> sumOfDigits(1234)
res0: Int = 10
scala> hasDigit(2)(1234)
res1: Boolean = true
scala> exists(_ == 2)(1234)
res2: Boolean = true
iterateAndAccumulate
6 = rec
| / \ |
| 0 6 |
| / \ |
| 1 + 5 |
| ------------
| 2| + 3 = acc |
--------| / \ |
| dig = 3 + 0 |
Control
Chapter 3
Control
Conditional construct
def _if
(cond: Boolean)
(block: () => Unit)
= cond match {
case true => block()
case false => ()
}
Conditional expression
scala> :l if.scala
Loading if.scala...
_if: (Boolean)(() => Unit)Unit
scala> _if(Math.random<0.5)(() =>
| println("ok")
| )
ok
Loop construct
def _while
(cond: () => Boolean)
(block: () => Unit): Unit
= if(cond()) {
block()
_while(cond)(block)
}
Loop expression
scala> :l while.scala
Loading while.scala...
_while: (() => Boolean)(() => Unit)Unit
scala> _while(() => Math.random<0.5)(() =>
| println ("ok")
| )
ok
Conditional construct
def _if
(cond: Boolean)
(block: => Unit)
= cond match {
case true => block
case false => ()
}
Loop construct
def _while
(cond: => Boolean)
(block: => Unit): Unit
= if(cond) {
block
_while(cond)(block)
}
Conditional expression
scala> :l if_cbn.scala
Loading if_cbn.scala...
_if: (Boolean)(=> Unit)Unit
scala> _if(Math.random<0.5) {
| println ("ok")
| }
ok
Loop expression
scala> :l while_cbn.scala
Loading while_cbn.scala...
_while: (=> Boolean)(=> Unit)Unit
scala> _while(Math.random<0.5) {
| println ("ok")
| }
ok
Types
Chapter 4
Types
Instance constants
class C {
val re = 0.0
val im = 0.0
}
Instance constants: usage
scala> :l complex01.scala
Loading complex01.scala...
defined class C
scala> new C
res0: C = C@151fe8a
toString
class C {
// ...
override def toString
= re + " + " + im + "*" + "i"
}
toString: usage
scala> :l complex02.scala
Loading complex02.scala...
defined class C
scala> new C
res0: C = 0.0 + 0.0*i
Class parameters
class C(r: Double, i: Double) {
val re = r
val im = i
// ...
}
Class parameters: usage
scala> :l complex03.scala
Loading complex03.scala...
defined class C
scala> new C(-1, -1)
res0: C = -1.0 + -1.0*i
toString again
override def toString = {
val sgnim = if(im<0.0){" - "}else{" + "}
val absim = if(im<0.0){ -im }else{ im }
if(im == 0.0) { re + "" } else {
if(re == 0.0) { im + "*i" } else {
re + sgnim + absim + "*i" } }
}
toString again: usage
scala> :l complex04.scala
Loading complex04.scala...
defined class C
scala> new C(-1, -1)
res0: C = -1.0 - 1.0*i
scala> new C(-1, +1)
res1: C = -1.0 + 1.0*i
Additive operators
class C(r: Double, i: Double) {
// ...
def +(c: C) =
new C(re + c.re, im + c.im)
def -(c: C) =
new C(re - c.re, im - c.im)
// ...
}
Additive operators: usage
scala> :l complex05.scala
Loading complex05.scala...
defined class C
scala> new C(+1,+1) + new C(-3,+2)
res1: C = -2.0 + 3.0*i
scala> new C(+1,+1) - new C(-3,+2)
res2: C = 4.0 - 1.0*i
Multiplicative operators
// ...
def *(c: C) = new C(re*c.re - im*c.im,
im*c.re + re*c.im)
def /(c: C) = {
val d = c.re*c.re + c.im*c.im
new C((re*c.re + im*c.im) / d,
(im*c.re - re*c.im) / d) }
// ...
Multiplicative operators: usage
scala> :l complex06.scala
Loading complex06.scala...
defined class C
scala> new C(+1,+1) * new C(-3,+2)
res0: C = -5.0 - 1.0*i
scala> new C(-5,-1) / new C(-3,+2)
res1: C = 1.0 + 1.0*i
Negation operator
class C(r: Double, i: Double) {
// ...
def unary_- = new C(-re, -im)
// ...
}
Negation operator: usage
scala> :l complex07.scala
Loading complex07.scala...
defined class C
scala> - new C(-5,-1)
res0: C = 5.0 + 1.0*i
scala> - new C(5,1)
res1: C = -5.0 - 1.0*i
The complex number i
class C(r: Double, i: Double) {
// ...
}
object C {
val i = new C(0.0, 1.0)
}
The complex number i: usage
scala> :l complex08.scala
Loading complex08.scala...
...
scala> import C.i
import C.i
scala> i * i
res0: C = -1.0
Converting Double
class C(r: Double, i: Double) {
// ...
}
object C {
//...
implicit def toC(d: Double)
= new C(d, 0.0)
}
Converting Double: usage
scala> :l complex09.scala
Loading complex09.scala...
...
scala> (1.0 + 1.0*i) / i
res0: C = 1.0 - 1.0*i
scala> 1.0 + 1.0*i / i
res1: C = 2.0
Spaces
Chapter 5
Spaces
Space
class Space[X] extends Actor {
// ...
}
Worker
abstract class Worker[X](space: Space[X])
extends Actor {
// ...
}
Work
object Types {
type Work[X] = PartialFunction[X,Unit]
}
Put and Reg
case class Put[X](x: X)
case class Reg[X](
work: Work[X]
worker: Worker[X])
App
case class App[X](work: Work[X], x: X)
Space information
private var ps: List[Put[X]] = Nil
private var rs: List[Reg[X]] = Nil
Space functionality: part one
I A worker can put objects x into the space
using space ! Put(x)
I A worker can register work with the space
using space ! Reg(work, worker)
Space functionality: part two
I A space can notify a worker when an objectx, to which the work he registered with thespace can be applied, has been put into thespace using worker ! App(work, x)
reacting to a Put: part one
case p@Put(x: X) => {
val frs =
rs filter(r => r.work.isDefinedAt(x)
if(frs == Nil) {
ps ::= p.asInstanceOf[Put[X]]
}
reacting to a Put: part two
else {
val fr = frs.last
rs = rs filter (r => !(r eq fr))
fr.worker ! App(fr.work, x)
}
reacting to a Reg: part one
case r@Reg(work, worker) => {
val fps =
ps filter (p => work.isDefinedAt(p.x))
if(fps == Nil) {
rs ::= r.asInstanceOf[Reg[X]]
}
reacting to a Reg: part two
else {
val fp = fps.last
ps = ps filter (p => !(p eq fp))
worker ! App(work, fp.x)
}
Worker: part one
abstract class Worker[X](space: Space[X])
extends Actor {
protected var isAcceptingMoreWork = true
protected def registerForWork()
def put(x: X) {
space ! Put(x) }
def reg(work: Work[X]) {
space ! Reg(work, this) }
Worker: part two
private def applyZeroOrMoreTimes() {
loop { react {
case App(work, x) => {
work.apply(x)
if(isAcceptingMoreWork) {
registerForWork()
} } } }
}
Worker: part three
private def applyOneOrMoreTimes() {
registerForWork()
applyZeroOrMoreTimes()
}
Worker: part four
def act() {
applyOneOrMoreTimes()
}
PingPong
sealed abstract class PingPong
case object Ping extends PingPong
case object Pong extends PingPong
case object Over extends PingPong
case object Done extends PingPong
Player
abstract class Player(
name: String
table: Space[PingPong]
)
extends Worker[PingPong](table) {
override def toString = name
}
Pinger: part one
protected def registerForWork() =
reg {
case Pong => {
if (Math.random < 0.95) {
println(this + " ping")
put(Ping)
} else {
put(Over)
}
Pinger: part two
case Done => {
println(this + " stop")
isAcceptingMoreWork = false
}
}
}
}
Ponger: part one
protected def registerForWork() =
reg {
case Ping => {
if (Math.random < 0.95) {
println(this + " pong")
put(Pong)
} else {
put(Over)
}
Ponger: part two
case Done => {
println(this + " stop")
isAcceptingMoreWork = false
}
}
}
}
Umpire: part one
class Umpire(
name: String,
players: List[Player],
table: Space[PingPong])
extends Worker[PingPong](table) {
override def toString = name
Umpire: part two
protected def registerForWork() =
reg {
case Over => {
println(this + " done")
for { _ <- players } put(Done)
println(this + " stop")
isAcceptingMoreWork = false
}
}
theTable
class Table extends Space[PingPong]
val theTable = new Table
thePlayers
val thePlayers =
Pinger("pinger_1", true) ::
Pinger("pinger_2", false) ::
Ponger("ponger_1") ::
Ponger("ponger_2") ::
Nil
theUmpire
val theUmpire =
new Umpire
("_umpire_", thePlayers, theTable)
main
theTable.start
thePlayers.foreach(_.start)
theUmpire.start
val startPinger = thePlayers.head
startPinger.put(Ping)
Top Related