Scala by Luc Duponcheel

91
Scala @ BeJUG Luc Duponcheel ImagineJ September 2, 2009

description

Scala introduction given at BeJUG

Transcript of Scala by Luc Duponcheel

Page 1: Scala by Luc Duponcheel

Scala @ BeJUG

Luc DuponcheelImagineJ

September 2, 2009

Page 2: Scala by Luc Duponcheel

Introduction

Chapter 0

Introduction

Page 3: Scala by Luc Duponcheel

Types

I Scala is a (taste)fully typed language

Page 4: Scala by Luc Duponcheel

Types

I Scala supports generic types

Page 5: Scala by Luc Duponcheel

Expressions

I Scala is an expression oriented language

Page 6: Scala by Luc Duponcheel

Objects

I Scala is an pure object oriented language

Page 7: Scala by Luc Duponcheel

Functions

I Scala is a functional language

Page 8: Scala by Luc Duponcheel

Scalable language

I Scala is a scalable languageI Scala does not grow on a language basisI Scala grows on a library basis

Page 9: Scala by Luc Duponcheel

Usage

Chapter 1

Usage

Page 10: Scala by Luc Duponcheel

Expression evaluation: Double

scala> 1.2.*(2.3)

res0: Double = 2.76

scala> 1.2 * 2.3

res1: Double = 2.76

Page 11: Scala by Luc Duponcheel

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

Page 12: Scala by Luc Duponcheel

Function result

def plus(x: Int) = (y: Int) => y + x

Page 13: Scala by Luc Duponcheel

Function result

scala> :l plus.scala

Loading plus.scala...

plus: (x: Int)(Int) => Int

scala> plus(1)

res0: (Int) => Int = <function1>

Page 14: Scala by Luc Duponcheel

Apply plus(1)

scala> plus(1).apply(1)

res1: Int = 2

scala> plus(1)(1)

res2: Int = 2

Page 15: Scala by Luc Duponcheel

Function parameter

def applyToZero[X](f: Int => X) = f(0)

Page 16: Scala by Luc Duponcheel

Function parameter

scala> :l applyToZero.scala

Loading applyToZero.scala...

applyToZero: [X]((Int) => X)X

scala> applyToZero (x => x + 1)

res0: Int = 1

Page 17: Scala by Luc Duponcheel

Function parameter and result

def twice[X](f: X => X)

= (x: X) => f(f(x))

Page 18: Scala by Luc Duponcheel

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

Page 19: Scala by Luc Duponcheel

Many parameter lists

def twice[X](f: X => X)(x: X) = f(f(x))

Page 20: Scala by Luc Duponcheel

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

Page 21: Scala by Luc Duponcheel

Placeholder syntax

scala> :l applyToZero.scala

Loading applyToZero.scala...

applyToZero: [X]((Int) => X)X

scala> applyToZero (_ + 1)

res0: Int = 1

Page 22: Scala by Luc Duponcheel

Patterns

Chapter 2

Patterns

Page 23: Scala by Luc Duponcheel

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

Page 24: Scala by Luc Duponcheel

Natural numbers and digits

type Nat = Int

type Dig = Int

Page 25: Scala by Luc Duponcheel

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))

}

}

Page 26: Scala by Luc Duponcheel

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(_) || _)

Page 27: Scala by Luc Duponcheel

Using structural recursion

scala> sumOfDigits(1234)

res0: Int = 10

scala> hasDigit(2)(1234)

res1: Boolean = true

scala> exists(_ == 2)(1234)

res2: Boolean = true

Page 28: Scala by Luc Duponcheel

unfoldAndThenFold

6

---- | / \ |

| dig = 3 + 3 = rec |

-----------------------

| 2 + 1 |

| / \ |

| 1 + 0 |

| / \ |

| 0 0 |

Page 29: Scala by Luc Duponcheel

List of digits

:

/ \

3 :

/ \

2 :

/ \

1 :

/

0

Page 30: Scala by Luc Duponcheel

Sum of list of digits

+

/ \

3 +

/ \

2 +

/ \

1 +

/ \

0 0

Page 31: Scala by Luc Duponcheel

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)

}

}

Page 32: Scala by Luc Duponcheel

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(_) || _)

Page 33: Scala by Luc Duponcheel

Using tail recursion

scala> sumOfDigits(1234)

res0: Int = 10

scala> hasDigit(2)(1234)

res1: Boolean = true

scala> exists(_ == 2)(1234)

res2: Boolean = true

Page 34: Scala by Luc Duponcheel

iterateAndAccumulate

6 = rec

| / \ |

| 0 6 |

| / \ |

| 1 + 5 |

| ------------

| 2| + 3 = acc |

--------| / \ |

| dig = 3 + 0 |

Page 35: Scala by Luc Duponcheel

Control

Chapter 3

Control

Page 36: Scala by Luc Duponcheel

Conditional construct

def _if

(cond: Boolean)

(block: () => Unit)

= cond match {

case true => block()

case false => ()

}

Page 37: Scala by Luc Duponcheel

Conditional expression

scala> :l if.scala

Loading if.scala...

_if: (Boolean)(() => Unit)Unit

scala> _if(Math.random<0.5)(() =>

| println("ok")

| )

ok

Page 38: Scala by Luc Duponcheel

Loop construct

def _while

(cond: () => Boolean)

(block: () => Unit): Unit

= if(cond()) {

block()

_while(cond)(block)

}

Page 39: Scala by Luc Duponcheel

Loop expression

scala> :l while.scala

Loading while.scala...

_while: (() => Boolean)(() => Unit)Unit

scala> _while(() => Math.random<0.5)(() =>

| println ("ok")

| )

ok

Page 40: Scala by Luc Duponcheel

Conditional construct

def _if

(cond: Boolean)

(block: => Unit)

= cond match {

case true => block

case false => ()

}

Page 41: Scala by Luc Duponcheel

Loop construct

def _while

(cond: => Boolean)

(block: => Unit): Unit

= if(cond) {

block

_while(cond)(block)

}

Page 42: Scala by Luc Duponcheel

Conditional expression

scala> :l if_cbn.scala

Loading if_cbn.scala...

_if: (Boolean)(=> Unit)Unit

scala> _if(Math.random<0.5) {

| println ("ok")

| }

ok

Page 43: Scala by Luc Duponcheel

Loop expression

scala> :l while_cbn.scala

Loading while_cbn.scala...

_while: (=> Boolean)(=> Unit)Unit

scala> _while(Math.random<0.5) {

| println ("ok")

| }

ok

Page 44: Scala by Luc Duponcheel

Types

Chapter 4

Types

Page 45: Scala by Luc Duponcheel

Instance constants

class C {

val re = 0.0

val im = 0.0

}

Page 46: Scala by Luc Duponcheel

Instance constants: usage

scala> :l complex01.scala

Loading complex01.scala...

defined class C

scala> new C

res0: C = C@151fe8a

Page 47: Scala by Luc Duponcheel

toString

class C {

// ...

override def toString

= re + " + " + im + "*" + "i"

}

Page 48: Scala by Luc Duponcheel

toString: usage

scala> :l complex02.scala

Loading complex02.scala...

defined class C

scala> new C

res0: C = 0.0 + 0.0*i

Page 49: Scala by Luc Duponcheel

Class parameters

class C(r: Double, i: Double) {

val re = r

val im = i

// ...

}

Page 50: Scala by Luc Duponcheel

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

Page 51: Scala by Luc Duponcheel

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" } }

}

Page 52: Scala by Luc Duponcheel

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

Page 53: Scala by Luc Duponcheel

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)

// ...

}

Page 54: Scala by Luc Duponcheel

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

Page 55: Scala by Luc Duponcheel

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) }

// ...

Page 56: Scala by Luc Duponcheel

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

Page 57: Scala by Luc Duponcheel

Negation operator

class C(r: Double, i: Double) {

// ...

def unary_- = new C(-re, -im)

// ...

}

Page 58: Scala by Luc Duponcheel

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

Page 59: Scala by Luc Duponcheel

The complex number i

class C(r: Double, i: Double) {

// ...

}

object C {

val i = new C(0.0, 1.0)

}

Page 60: Scala by Luc Duponcheel

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

Page 61: Scala by Luc Duponcheel

Converting Double

class C(r: Double, i: Double) {

// ...

}

object C {

//...

implicit def toC(d: Double)

= new C(d, 0.0)

}

Page 62: Scala by Luc Duponcheel

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

Page 63: Scala by Luc Duponcheel

Spaces

Chapter 5

Spaces

Page 64: Scala by Luc Duponcheel

Space

class Space[X] extends Actor {

// ...

}

Page 65: Scala by Luc Duponcheel

Worker

abstract class Worker[X](space: Space[X])

extends Actor {

// ...

}

Page 66: Scala by Luc Duponcheel

Work

object Types {

type Work[X] = PartialFunction[X,Unit]

}

Page 67: Scala by Luc Duponcheel

Put and Reg

case class Put[X](x: X)

case class Reg[X](

work: Work[X]

worker: Worker[X])

Page 68: Scala by Luc Duponcheel

App

case class App[X](work: Work[X], x: X)

Page 69: Scala by Luc Duponcheel

Space information

private var ps: List[Put[X]] = Nil

private var rs: List[Reg[X]] = Nil

Page 70: Scala by Luc Duponcheel

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)

Page 71: Scala by Luc Duponcheel

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)

Page 72: Scala by Luc Duponcheel

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]]

}

Page 73: Scala by Luc Duponcheel

reacting to a Put: part two

else {

val fr = frs.last

rs = rs filter (r => !(r eq fr))

fr.worker ! App(fr.work, x)

}

Page 74: Scala by Luc Duponcheel

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]]

}

Page 75: Scala by Luc Duponcheel

reacting to a Reg: part two

else {

val fp = fps.last

ps = ps filter (p => !(p eq fp))

worker ! App(work, fp.x)

}

Page 76: Scala by Luc Duponcheel

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) }

Page 77: Scala by Luc Duponcheel

Worker: part two

private def applyZeroOrMoreTimes() {

loop { react {

case App(work, x) => {

work.apply(x)

if(isAcceptingMoreWork) {

registerForWork()

} } } }

}

Page 78: Scala by Luc Duponcheel

Worker: part three

private def applyOneOrMoreTimes() {

registerForWork()

applyZeroOrMoreTimes()

}

Page 79: Scala by Luc Duponcheel

Worker: part four

def act() {

applyOneOrMoreTimes()

}

Page 80: Scala by Luc Duponcheel

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

Page 81: Scala by Luc Duponcheel

Player

abstract class Player(

name: String

table: Space[PingPong]

)

extends Worker[PingPong](table) {

override def toString = name

}

Page 82: Scala by Luc Duponcheel

Pinger: part one

protected def registerForWork() =

reg {

case Pong => {

if (Math.random < 0.95) {

println(this + " ping")

put(Ping)

} else {

put(Over)

}

Page 83: Scala by Luc Duponcheel

Pinger: part two

case Done => {

println(this + " stop")

isAcceptingMoreWork = false

}

}

}

}

Page 84: Scala by Luc Duponcheel

Ponger: part one

protected def registerForWork() =

reg {

case Ping => {

if (Math.random < 0.95) {

println(this + " pong")

put(Pong)

} else {

put(Over)

}

Page 85: Scala by Luc Duponcheel

Ponger: part two

case Done => {

println(this + " stop")

isAcceptingMoreWork = false

}

}

}

}

Page 86: Scala by Luc Duponcheel

Umpire: part one

class Umpire(

name: String,

players: List[Player],

table: Space[PingPong])

extends Worker[PingPong](table) {

override def toString = name

Page 87: Scala by Luc Duponcheel

Umpire: part two

protected def registerForWork() =

reg {

case Over => {

println(this + " done")

for { _ <- players } put(Done)

println(this + " stop")

isAcceptingMoreWork = false

}

}

Page 88: Scala by Luc Duponcheel

theTable

class Table extends Space[PingPong]

val theTable = new Table

Page 89: Scala by Luc Duponcheel

thePlayers

val thePlayers =

Pinger("pinger_1", true) ::

Pinger("pinger_2", false) ::

Ponger("ponger_1") ::

Ponger("ponger_2") ::

Nil

Page 90: Scala by Luc Duponcheel

theUmpire

val theUmpire =

new Umpire

("_umpire_", thePlayers, theTable)

Page 91: Scala by Luc Duponcheel

main

theTable.start

thePlayers.foreach(_.start)

theUmpire.start

val startPinger = thePlayers.head

startPinger.put(Ping)