32
Where objects and functions meet by Mario Fusco [email protected] twitter: @mariofusco

Scala - where objects and functions meet

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Scala - where objects and functions meet

Where objects and functions meet

by Mario [email protected]: @mariofusco

Page 2: Scala - where objects and functions meet

day 1: hour 1: Object Orientation Classes, Objects and Traits Generic Types Case Classes, Patter Matching and Tuples hour 2: Functional Programming First-class and Anonymous Functions Higer-Order Functions and Curry Implicit Parameters and Conversions Using Scala features to create a simple DSLday 2: hour 1: Using OO and FP together Structural Typing Scala Collections For-Comprehensions Options and Monads hour 2: Concurrency Abstractions for Concurrency Actors and Remote Actors

Page 3: Scala - where objects and functions meet

Do we need a new language?

Keep It SimpleVs.

Do More With Less

Page 4: Scala - where objects and functions meet

Why Scala?

object-oriented

functional

Java compatible

concise

extensible

statically typed

concurrent

scriptable

Page 5: Scala - where objects and functions meet

The first Scala classclass Rational(n: Int, d: Int) {

val num = n val den = d

def this(n: Int) = this(n, 1)

def + (that: Rational): Rational = new Rational(num * that.den + that.num * den, den * that.den)

def + (i: Int): Rational = new Rational(num + i * den, den)

override def toString = "" + num + "/" + den}

Page 6: Scala - where objects and functions meet

class Rational(n: Int = 1, d: Int = 1) extends AnyRef { ....}

Rational(n = 2, d = 3)

Rational(d = 3, n = 2)

Rational(d = 3)Rational()

Named and default parameters

Page 7: Scala - where objects and functions meet

Scala’s type hierarchy

Page 8: Scala - where objects and functions meet

Object

object RationalOne extends Rational(1)

object Rational { def apply(n: Int) = new Rational(n) def apply(n: Int, d: Int) = new Rational(n, d)}

val one = RationalOneval two = Rational(1) + one

(Companion)

val two = Rational(1).+(one)

Page 9: Scala - where objects and functions meet

Traits

class Animal { def eat(): Unit }trait Mammal extends Animal { def giveBirth(): Mammal }trait HasWings extends Animal { def fly(): Unit }trait HasLegs extends Animal { def walk(): Unit }

class Snake extends Animalclass Frog extends Animal with HasLegsclass Cat extends Animal with Mammal with HasLegsclass Bat extends Animal with Mammal with HasWingsclass Chimera extends Animal with Mammal with HasWings with HasLegs

Page 10: Scala - where objects and functions meet

Let’s put all togethertrait IntSet { def contains(x: Int): Boolean def notContains(x: A) = !contains(x) def add(x: Int): IntSet}

object EmptySet extends IntSet { def contains(x: Int): Boolean = false def add(x: Int): IntSet = new NonEmptySet(x, EmptySet, EmptySet)}

class NonEmptySet(elem: Int, left: IntSet, right: IntSet) extends IntSet { def contains(x: Int): Boolean = if (x < elem) left contains x else if (x > elem) right contains x else true def add(x: Int): IntSet = if (x < elem) new NonEmptySet(elem, left add x, right) else if (x > elem) new NonEmptySet(elem, left, right add x) else this}

Page 11: Scala - where objects and functions meet

Generic Typestrait Set[A <: Ordered[A]] { def contains(x: A): Boolean def add(x: A): Set[A]}

class EmptySet[A <: Ordered[A]] extends Set[A] { def contains(x: A): Boolean = false def add(x: A): Set[A] = new NonEmptySet(x, new EmptySet[A], new EmptySet[A])}

class NonEmptySet[A <: Ordered[A]] (elem: A, left: Set[A], right: Set[A]) extends Set[A] { def contains(x: A): Boolean = if (x < elem) left contains x else if (x > elem) right contains x else true def add(x: A): Set[A] = if (x < elem) new NonEmptySet(elem, left add x, right) else if (x > elem) new NonEmptySet(elem, left, right add x) else this}

Page 12: Scala - where objects and functions meet

Case classes

sealed trait Exprcase class Var(name: String) extends Exprcase class Number(num: Double) extends Exprcase class Unop(op: String, arg: Expr) extends Exprcase class Binop(op: String, l: Expr, r: Expr) extends Expr

Page 13: Scala - where objects and functions meet

Pattern matching

def simplify(expr: Expr): Expr = expr match { case Unop("-", Unop("-", e)) => e // Double negation case Binop("+", e, Number(0)) => e // Adding zero case Binop("*", e, Number(1)) => e // Multiplying by one case _ => expr}

// Simplify double negation: simplified = Var("x")val simplified = simplify(Unop("-", Unop("-", Var("x"))))

Page 14: Scala - where objects and functions meet

Tuples

val pair = (2, "items")println(pair._1) // prints 2println(pair._2) // prints items

def divmod(x: Int, y: Int): (Int, Int) = (x / y, x % y)

divmod(x, y) match { case (n, d) => println("quotient: " + n + ", rest: " + d)}

Page 15: Scala - where objects and functions meet

First-Class Functiondef sum(f: Int => Int, a: Int, b: Int): Int = if (a > b) 0 else f(a) + sum(f, a + 1, b)

def id(x: Int): Int = xdef sumInts(a: Int, b: Int): Int = sum(id, a, b)

def square(x: Int): Int = x * xdef sumSquares(a: Int, b: Int): Int = sum(square, a, b)

def sumInts(a: Int, b: Int): Int = if (a > b) 0 else a + sumInts(a + 1, b)

def sumSquares(a: Int, b: Int): Int = if (a > b) 0 else a * a + sumSquares(a + 1, b)

def sumInts(a: Int, b: Int): Int = sum((x: Int) => x, a, b)def sumSquares(a: Int, b: Int): Int = sum((x: Int) => x * x, a, b)

Anonymous Function

Page 16: Scala - where objects and functions meet

Higher-Order Functions and Currydef sum(f: Int => Int): (Int, Int) => Int = { def sumF(a: Int, b: Int): Int = if (a > b) 0 else f(a) + sumF(a + 1, b) sumF}

def sumInts = sum(x => x)def sumSquares = sum(x => x * x)

val sum1To10 = sumInts(1, 10)

val sum1To10 = sum(x => x)(1, 10)

def sum(f: Int => Int)(a: Int, b: Int): Int = if (a > b) 0 else f(a) + sum(f)(a + 1, b)

def sum(a: Int, b: Int)(f: Int => Int): Int = if (a > b) 0 else f(a) + sum(a + 1, b)(f)

val sum1To10 = sum(1, 10) { x => x }

Page 17: Scala - where objects and functions meet

Implicit parametersabstract class Aggregator[A] { def unit: A def add(x: A, y: A): A}object stringAggregator extends Aggregator[String] { def unit = "" def add(x: String, y: String): String = x concat y}object intAggregator extends Aggregator[Int] { def unit = 0 def add(x: Int, y: Int): Int = x + y}

def sum[A](l: List[A]) (a: Aggregator[A]): A = if (l.isEmpty) a.unit else a.add(l.head, sum(l.tail)(a))

sum(List("a", "b", "c"))(stringAggregator)sum(List(1, 2, 3))(intAggregator)

(implicit a: Aggregator[A]): A =

Page 18: Scala - where objects and functions meet

Implicit conversion

val a = new Rational(2, 3)val b = a + 2 // = 8/3val c = 2 + a // Compilation Error

implicit def intToRational(x: Int) = new Rational(x)val c = 2 + a // = 8/3

View bound

trait Set[A <% Rational]

Page 19: Scala - where objects and functions meet

Structural Typing(duck typing done right)

class Duck { quack() { println "quack" } }doQuack(new Duck)

doQuack(d) { d.quack() }

class Dog { barf() { println "barf" } }doQuack(new Dog)

def doQuack(d:{ def quack():Unit }) = d.quack()

class Duck { def quack() = println "quack" }doQuack(new Duck)

class Dog { def barf() = println "barf" }doQuack(new Dog)runtime error

compilationerror

Duck typing is the dynamic mechanism that allows to discover a dog cannot say quack only at runtime ... in production ... on friday evening

Page 20: Scala - where objects and functions meet

Listsval letters: List[String] = List("a", "b", "c", "d")

val emptyList = Nil

val letters = "a" :: "b" :: "c" :: "d" :: Nil

x :: ys is equivalent to ys.::(x) // infix operator == right associative

xs ::: ys is equivalent to ys.:::(xs)

letters.head = "a"letters.tail = List("b", "c", "d")

def sortedInsert(x: Int, xs: List[Int]): List[Int] = xs match { case List() => List(x) case y :: ys => if (x <= y) x :: xs else y :: sortedInsert(x, ys)}

Page 21: Scala - where objects and functions meet

Higher-Order Functions on Lists

val animals = List("dog", "cat", "horse", "rabbit")animals.foreach(s => println(s))

def foreach(f: A => Unit) { this match { case Nil => () case x :: xs => f(x); xs.foreach(f) }}

animals.foreach(println _)animals.foreach(println)

animals.map(s => s + "s")animals.mkString(", ")animals.count(s => s.length > 3)animals.remove(s => s.length > 3)animals.sort((s,t) => s.charAt(1) < t.charAt(1))

animals.foldLeft(0)((s,t) => s + t.length)(0 /: animals)(_ + _.length)

Page 22: Scala - where objects and functions meet

For-Comprehensions

for (p <- persons if p.age > 20) yield p.name

persons filter (p => p.age > 20) map (p => p.name)

for { p <- persons // Generators c <- p.children if c.name startsWith "A" // Filter} yield p.name // Map

Page 23: Scala - where objects and functions meet

Given n>0 find all pairs i and j where 1 ≤ j ≤ i ≤ n and i+j is prime

List.range(1, n) .map(i => List.range(1, i).map(x => (i, x))) .foldRight(List[(Int, Int)]()) {(xs, ys) => xs ::: ys} .filter(pair => isPrime(pair._1 + pair._2))

List.range(1, n) .flatMap(i => List.range(1, i).map(x => (i, x))) .filter(pair => isPrime(pair._1 + pair._2))

Where: class List[A] { def flatMap[B](f: A => List[B]): List[B] }

for { i <- List.range(1, n) j <- List.range(1, i) if isPrime(i + j) } yield {i, j}

List.range(1, n) .flatMap(i => List.range(1, i) .filter(j => isPrime(i+j)) .map(j => (i, j)))

Page 24: Scala - where objects and functions meet

Options

Tony Hoare, who invented the null reference in 1965 while working on an object oriented language called ALGOL W, called

its invention his “billion dollar mistake”

val capitals = Map("Italy" -> "Rome", "Switzerland" -> "Bern", "Germany" -> "Berlin" , "France" -> "Paris")

println(capitals.get("Italy")) // Some(Rome)println(capitals.get("Spain")) // None

println(capitals.get("Italy").get) // Romeprintln(capitals.get("Spain").get) // thorws Exceptionprintln(capitals.get("Spain").getOrElse("Unknown")) // Unknown

Page 25: Scala - where objects and functions meet

Options as Monadsdef map[B](f: A => B): M[B]def flatMap[B](f: A => M[B]): M[B]def filter(p: A => Boolean): M[A]

def readPositiveIntParam(params: Map[String, String], name: String): Int = params get name flatMap stringToInt filter (_ > 0) getOrElse 0

def stringToInt(string: String) : Option[Int] = try { Some(string.toInt)} catch { case _ : java.lang.NumberFormatException => None}

def readPositiveIntParam(params: Map[String, String], name: String): Int = (for { param <- params get name; value <- stringToInt(param) if (value > 0) } yield value) getOrElse 0

val params = Map("a" -> "5", "b" -> "false", "c" -> "-3")val readPositiveIntParam(params, "a") // == 5val readPositiveIntParam(params, "b") // == 0 – Same for "c" and "d"

Page 26: Scala - where objects and functions meet

Signals and Monitors

def synchronized[A] (e: => A): Adef wait()def wait(msec: Long)def notify()def notifyAll()

class SyncVar[A] { private var isDefined: Boolean = false private var value: A = _ def get = synchronized { while (!isDefined) wait() value } def set(x: A) = synchronized { value = x; isDefined = true; notifyAll() } def isSet: Boolean = synchronized { isDefined } def unset = synchronized { isDefined = false }}

SyncVar

Page 27: Scala - where objects and functions meet

Futuresdef future[A](p: => A): Unit => A = { val result = new SyncVar[A] fork { result.set(p) } (() => result.get)}

val x = future(someLengthyComputation)anotherLengthyComputationval y = f(x()) + g(x())

class Lock { var available = true def acquire = synchronized { while (!available) wait() available = false } def release = synchronized { available = true notify() }}

Semaphores

class MailBox { def send(msg: Any) def receive[A](f: PartialFunction[Any, A]): A def receiveWithin[A](msec: Long)(f: PartialFunction[Any, A]): A}

Mailboxes

Page 28: Scala - where objects and functions meet

Actors

class PrinterActor extends Actor { def act() { while(true) { receive { case msg => println("Received message: " + msg) } } }}

val printerActor = new PrinterActorprinterActor.start

printerActor ! "hi there“ // prints "Received message: hi there"printerActor ! 23 // prints "Received message: 23"

Page 29: Scala - where objects and functions meet

import scala.actors.Actor._

val printerActor = actor { while(true) { receive { case s: String => println("I got a String: " + s) case i: Int => println("I got an Int: " + i.toString) case _ => println(" I don’t know what I got ") } }}

printerActor ! "hi there" // prints “I got a String: hi there”printerActor ! 23 // prints “I got an Int: 23”printerActor ! 3.33 // prints “I don’t know what I got”

Creating Actors with the actor method

Page 30: Scala - where objects and functions meet

react instead of receive (when possible)

import scala.actors.Actor._

val printerActor = actor { loop { react { case s: String => println("I got a String: " + s) case i: Int => { println("I got an Int: " + i.toString) println(“Waiting for another Int") react { case j: Int => println(“Another Int: " + j.toString) } } case _ => exit } }}

Page 31: Scala - where objects and functions meet

Message types

! send an asynchronous message which means that the sending actor does not wait until the message is received; its execution continues immediately. All actors have a mailbox which buffers incoming messages until they are processed

!? send a synchronous message: causes the sending actor to wait until a response is received which is then returned. There is an overloaded variant taking a timeout and returning an Option[Any] instead of Any

!! similar to !? In the sense that it allows to get an answer from the receiver. However, instead of blocking the sending actor until a response is received, it returns Future instance that can be used to retrieve the receiver’s response once it is available

Page 32: Scala - where objects and functions meet

Remote Actorsactor { // SERVER ACTOR RemoteActor.classLoader = getClass().getClassLoader() alive(9000) // starts remote actor listening on the given port register('Server, self) // registers the actor using the symbol loop { receive { case Message => sender ! ... } }}

actor { // CLIENT ACTOR trapExit = true // listens exit of linked actors RemoteActor.classLoader = getClass().getClassLoader() alive(9001) val server = select(Node("127.0.0.1", 9000), 'Server) link(server) // links this actor to the server one server ! Message // sends a Message to the server}