79
From Java to Scala - advantages and possible risks Oleksii Petinov Scala engineer, Newground

From Java to Scala - advantages and possible risks

Embed Size (px)

Citation preview

Page 1: From Java to Scala - advantages and possible risks

From Java to Scala - advantages and possible risks

Oleksii PetinovScala engineer,

Newground

Page 2: From Java to Scala - advantages and possible risks

Scala = “scalable”

● Scalable = extensiblesmall core – everything else are libraries. Efficient for both small utilities or large-scale systems. Very good for building libraries (Play, Akka etc)

var capital = Map("US" -> "Washington", "France" -> "Paris")

capital += ("Japan" -> "Tokyo")

println(capital("France"))

● Multiparadigm = FP + OOP

Page 3: From Java to Scala - advantages and possible risks

Who is using Scala

● Twitter

● Foursquare

● UK Guardian

● Sony Pictures Entertainment

● SAP

● Xerox

● Novell

● Netflix

● Tumblr (partially)

Page 4: From Java to Scala - advantages and possible risks

Compatibility and learning

● Very good Java interop● Easy to get into – allows (almost) Java style

programming

Page 5: From Java to Scala - advantages and possible risks

Concise

Java:

class MyClass {

private int index;

private String name;

public MyClass(int index, String name) {

this.index = index;

this.name = name;

}

}

Scala:

case class MyClass(index: Int, name: String)

Page 6: From Java to Scala - advantages and possible risks

High-level

Java:

boolean nameHasUpperCase = false;

for (int i = 0; i < name.length(); ++i) {

if (Character.isUpperCase(name.charAt(i))) {

nameHasUpperCase = true;

break;

}

}

Scala:

val nameHasUpperCase = name.exists(_.isUpper)

Page 7: From Java to Scala - advantages and possible risks

Staticly-typed

● Advanced static type system

● Verifiable properties

● Sophisticated type inference system

val x = new HashMap[Int, String]()

val x: Map[Int, String] = new HashMap()

● Duck typing

def callSpeak[A <: { def speak(): Unit }](obj: A) {

// code here ...

obj.speak()

}

Page 8: From Java to Scala - advantages and possible risks

FunctionalWith side-effects

class Cafe {

def buyCoffee(cc: CreditCard): Coffee = {

val cup = new Coffee()

cc.charge(cup.price)

cup

}

}

With side-effects and payments object:

class Cafe {

def buyCoffee(cc: CreditCard, p: Payments): Coffee = {

val cup = new Coffee()

p.charge(cc, cup.price)

cup

}

}

Side-effects free – returning Charge object:

class Cafe {

def buyCoffee(cc: CreditCard): (Coffee, Charge) = {

val cup = new Coffee()

(cup, Charge(cc, cup.price))

}

}

Page 9: From Java to Scala - advantages and possible risks

Small core, extensibility

Library class `scala.BigInt` feels natural – like part of the language:

def factorial(x: BigInt): BigInt = if (x == 0) 1 else x * factorial(x - 1)

In Java it feels like more like library type:

import java.math.BigInteger

def factorial(x: BigInteger): BigInteger =

if (x == BigInteger.ZERO)

BigInteger.ONE

else

x.multiply(factorial(x.subtract(BigInteger.ONE)))

Page 10: From Java to Scala - advantages and possible risks

High level - predicates

def sort(xs: Array[Int]): Array[Int] = {

if (xs.length <= 1) xs

else {

val pivot = xs(xs.length / 2)

Array.concat(

sort(xs filter (pivot >)),

xs filter (pivot ==),

sort(xs filter (pivot <)))

}

}

Page 11: From Java to Scala - advantages and possible risks

High level – control structuresDefinition (open a resource, operate on it, and then close the resource):

def withPrintWriter(file: File)(op: PrintWriter => Unit) {

val writer = new PrintWriter(file)

try {

op(writer)

} finally {

writer.close()

}

}

Usage:

val file = new File("date.txt")

withPrintWriter(file) {

writer => writer.println(new java.util.Date)

}

Page 12: From Java to Scala - advantages and possible risks

DSLs (internal)Scalatest:

test("pop is invoked on an empty stack") { val emptyStack = new Stack[String]

evaluating { emptyStack.pop() } should produce [NoSuchElementException]

emptyStack should be ('empty)

}

Squeryl:

def songCountByArtistId: Query[GroupWithMeasures[Long,Long]] =

from(artists, songs)((a,s) =>

where(a.id === s.artistId)

groupBy(a.id)

compute(count)

)

Page 13: From Java to Scala - advantages and possible risks

Traits trait Philosophical {

def philosophize() {

println("I consume memory, therefore I am!")

}

}

class Animal

trait HasLegs

class Frog extends Animal with Philosophical with HasLegs {

override def toString = "green"

}

Page 14: From Java to Scala - advantages and possible risks

Inheritance “diamond” problem

Page 15: From Java to Scala - advantages and possible risks

Operator notation for methods

scala> val sum = 1 + 2 // Scala invokes (1).+(2)

sum: Int = 3

scala> val a = List(1,2,3)

a: List[Int] = List(1, 2, 3)

scala> val b = List(4,5,6)

b: List[Int] = List(4, 5, 6)

scala> a ::: b // Scala invokes b.:::(a)

res0: List[Int] = List(1, 2, 3, 4, 5, 6)

Page 16: From Java to Scala - advantages and possible risks

Local functions

def processFile(filename: String, width: Int) { def processLine(line: String) {

if (line.length > width)

println(filename +": "+ line)

}

val source = Source.fromFile(filename)

for (line <- source.getLines())

processLine(line)

}

Page 17: From Java to Scala - advantages and possible risks

Function literals and values

● Function literals and valuesscala> val increase = (x: Int) => x + 1

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

scala> increase(10)

res0: Int = 11

● Function literals as predicatessomeNumbers.filter(x => x > 0)

Page 18: From Java to Scala - advantages and possible risks

Named and default arguments● Named

scala> def speed(distance: Float, time: Float): Float = distance / time

speed: (distance: Float,time: Float)Float

scala> speed(100, 10)

res28: Float = 10.0

scala> speed(time = 10, distance = 100)

res30: Float = 10.0

● Default

def printTime(out: java.io.PrintStream = Console.out) =

out.println("time = "+ System.currentTimeMillis())

Page 19: From Java to Scala - advantages and possible risks

Handling errors: exceptions def purchaseCoffee(money: Int): Coffee =

brewCoffee(buyBeans(money))

def buyBeans(money: Int): Beans =

if (money < price)

throw new Exception(s"Not enough money to buy beans for a coffee, need $price")

else

new Beans

def brewCoffee(beans: Beans): Coffee = {

// simulate a faulty grinder that fails 25% of the time

if (Math.random < 0.25)

throw new Exception("Faulty grinder failed to grind beans!")

else

new Coffee

}

Page 20: From Java to Scala - advantages and possible risks

Handling exceptions functionally case class FailureReason(reason: String)

def purchaseCoffee(money: Int): Either[FailureReason, Coffee] =

for {

beans <- buyBeans(money).right

coffee <- brewCoffee(beans).right

} yield coffee

def buyBeans(money: Int): Either[FailureReason, Beans] =

if (money < price)

Left(FailureReason(s"Not enough money to buy beans for a coffee, need $price"))

else

Right(new Beans)

def brewCoffee(beans: Beans): Either[FailureReason, Coffee] = {

if (Math.random < 0.25)

Left(FailureReason("Faulty grinder failed to grind beans!"))

else

Right(new Coffee)

}

Page 21: From Java to Scala - advantages and possible risks

Partially applied functionsOriginal function:

def withTax(cost: Float, state: String) = { /* Some complicated lookup table */ }

Partially applied function:

val locallyTaxed = withTax(_: Float, "NY")

val costOfApples = locallyTaxed(price("apples"))

Converting method to function value:

val func = method //Wrong

val func :Int => Int = method //This works

val func = method _ //Or like this

Page 22: From Java to Scala - advantages and possible risks

Scala complexity● Scala is not complex, but it allows you to compactly

express complex ideas. ● Understanding compact expression of complex

ideas can be hard.val x:Option[Int] = 2.some // scalaz enrichment for options

val y:Option[Int] = 3.some

val z:Option[Int] = 5.some

// With scalaz we can do the following instead of for or maps

// First we need to put the function in the right form, curried.

// To understand why please read the references I've given below.

val addInts = ( (a:Int, b:Int, c:Int) => a + b + c ).curried

val sum = x <*> (y <*> (z map addInts)) // Some(10)

Page 23: From Java to Scala - advantages and possible risks

Tail recursion

Tail-recursive function:

@tailrec

def approximate(guess: Double): Double =

if (isGoodEnough(guess)) guess

else approximate(improve(guess))

}

Compiles to loop and performance-wise is the same as:

def approximateLoop(initialGuess: Double): Double = {

var guess = initialGuess

while (!isGoodEnough(guess))

guess = improve(guess)

guess

}

Page 24: From Java to Scala - advantages and possible risks

Control absatractions private def filesHere = new java.io.File(".").listFiles

def filesMatching(query: String,

matcher: (String, String) => Boolean) = {

for (file <- filesHere; if matcher(file.getName, query))

yield file

}

def filesEnding(query: String) =

filesMatching(query, _.endsWith(_))

def filesContaining(query: String) =

filesMatching(query, _.contains(_))

def filesRegex(query: String) =

filesMatching(query, _.matches(_))

Page 25: From Java to Scala - advantages and possible risks

Currying (see slide 11)

scala> def curriedSum(x: Int)(y: Int) = x + y

curriedSum: (x: Int)(y: Int)Int

scala> def first(x: Int) = (y: Int) => x + y

first: (x: Int)(Int) => Int

scala> val second = first(1)

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

scala> second(2)

res6: Int = 3

Page 26: From Java to Scala - advantages and possible risks

Scala types (everything is object)

Page 27: From Java to Scala - advantages and possible risks

Objects – natural singletones abstract class Browser {

val database: Database

def recipesUsing(food: Food) =

database.allRecipes.filter(recipe =>

recipe.ingredients.contains(food))

}

abstract class Database {

def allFoods: List[Food]

}

object SimpleDatabase extends Database {

def allFoods = List(Apple, Orange, Cream, Sugar)

}

object SimpleBrowser extends Browser {

val database = SimpleDatabase

}

Page 28: From Java to Scala - advantages and possible risks

Enumerations and case objects

object Currency extends Enumeration {

val GBP = Value("GBP")

val EUR = Value("EUR") //etc.

}

sealed trait Currency { def name: String }

case object EUR extends Currency { val name = "EUR" } //etc.

case class UnknownCurrency(name: String) extends Currency

currency match {

case EUR =>

case UnknownCurrency(code) =>

}

Page 29: From Java to Scala - advantages and possible risks

Dependency Injection

● Non-framework DI Patterns (constructor, setter, cake, thin-cake, implicits, scalaz.Reader monad)

● DI frameworks (spring, guice, Subcut, MacWire, Scaldi)

Page 30: From Java to Scala - advantages and possible risks

Package objects // In file bobsdelights/package.scala

package object bobsdelights {

def showFruit(fruit: Fruit) {

import fruit._

println(name +"s are "+ color)

}

}

// In file PrintMenu.scala

package printmenu

import bobsdelights.showFruit

object PrintMenu {

def main(args: Array[String]) {

for (fruit <- Fruits.menu) {

showFruit(fruit)

}

}

}

Page 31: From Java to Scala - advantages and possible risks

Case classes abstract class Expr

case class Var(name: String) extends Expr

case class Number(num: Double) extends Expr

case class UnOp(operator: String, arg: Expr) extends Expr

case class BinOp(operator: String, left: Expr, right: Expr) extends Expr

scala> val v = Var("x")

v: Var = Var(x)

scala> val op = BinOp("+", Number(1), v)

op: BinOp = BinOp(+,Number(1.0),Var(x))

scala> v.name

res0: String = x

scala> println(op)

BinOp(+,Number(1.0),Var(x))

scala> op.right == Var("x")

res3: Boolean = true

scala> op.copy(operator = "-")

res4: BinOp = BinOp(-, Number(1.0),Var(x))

Page 32: From Java to Scala - advantages and possible risks

Pattern matching expr match {

case BinOp(op, left, right) =>

println(expr + " is a binary operation")

case somethingElse => "not expression: "+ somethingElse

case _ =>

}

case 0 => "zero"

case BinOp("+", e, Number(0)) =>

println("a deep match")

case List(0, _, _) => println("found it")

case s: String => s.length

case BinOp("+", x, y) if x == y =>

BinOp("*", x, Number(2))

Page 33: From Java to Scala - advantages and possible risks

Covering all cases

Sealed classes sealed abstract class Expr

case class Var(name: String) extends Expr

case class Number(num: Double) extends Expr

case class UnOp(operator: String, arg: Expr) extends Expr

case class BinOp(operator: String, left: Expr, right: Expr) extends Expr

Page 34: From Java to Scala - advantages and possible risks

Option instead of null scala> var x : Option[String] = None

x: Option[String] = None

scala> x.get

java.util.NoSuchElementException: None.get in

scala> x.getOrElse("default")

res0: String = default

scala> x = Some("Now Initialized")

x: Option[String] = Some(Now Initialized)

scala> x.get

res0: java.lang.String = Now Initialized

scala> x.getOrElse("default")

res1: java.lang.String = Now Initialized

val myData = map.get(userId).map(doFunction).map(toHtml)

println(myData.getOrElse(noDataHtml))

Page 35: From Java to Scala - advantages and possible risks

Lists

val oneTwo = List(1, 2)

val threeFour = List(3, 4)

val oneTwoThreeFour = oneTwo ::: threeFour

val twoThree = List(2, 3)

val oneTwoThree = 1 :: twoThree

val oneTwoThree = 1 :: 2 :: 3 :: Nil

list.head // Returns the first element in the thrill list

list.init // Returns a list of all but the last element in the thrill list

list.tail // Returns the thrill list minus its first element

Page 36: From Java to Scala - advantages and possible risks

Scala collections: uniformity

Page 37: From Java to Scala - advantages and possible risks

HOF and collections - 1 {

val names = Array("Sam", "Pamela", "Dave", "Pascal", "Erik")

val filteredNames = names filter(_.contains(”am”)) toList

}

{

val names = Array("Sam", "Pamela", "Dave", "Pascal", "Erik")

val nameList = names.zipWithIndex collect {

case (c, index) if (c.length <= index+1) => c

} toList

}

{

val nameList1 = List("Anders", "David", "James", "Jeff", "Joe", "Erik")

nameList1 foreach { n => println(s"Hello! $n") }

}

{

val map = Map("UK" -> List("Bermingham", "Bradford", "Liverpool"),

"USA" -> List("NYC", "New Jersey", "Boston", "Buffalo"))

val cities = map.values.flatten

}

{

val numbers = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)

val first4 = numbers take(4) toList

}

Page 38: From Java to Scala - advantages and possible risks

HOF on collections - 2 {

val moreNames = Array("Sam", "Samuel", "Dave", "Pascal", "Erik", "Sid")

val sNames = moreNames takeWhile(_ startsWith "S") toList

}

{

val vipNames = Array("Sam", "Samuel", "Samu", "Remo", "Arnold", "Terry")

val skippedList = vipNames drop(3) toList

}

{

val numbers = Seq(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 20)

val skippedList = numbers dropWhile(_ < 10)

}

{

val friends = Array("Sam", "Pamela", "Dave", "Anders", "Erik")

val sortedFriends = friends.sorted

}

{

val friends = Array("Sam", "Pamela", "Dave", "Anders", "Erik")

friends = friends.sortBy(_.length)

}

Page 39: From Java to Scala - advantages and possible risks

HOF and collections - 3

val myMap = Map("Brown Bear" -> 635, "Grizzly Bear" -> 360, "American Black Bear" -> 270, "Polar Bear" -> 680)

myMap.filter((x) => x._2 > 300)

myMap.filter(_._2 > 300)

myMap.filter { case (x, y) => y > 300 }

myMap.foldLeft(0)((sum, v) => sum + v._2) / myMap.size

val animals = Set("newt", "armadillo", "cat", "guppy")

animals.foreach(println)

val lengthsMapped = animals.map(animal => animal.length)

val nums = Set(1,2,3,4)

nums.map(x=>x+1).map(x=>x*x)

val nums2 = nums.map(x=>x+1)

nums2.map(x=>x*x)

nums.map(x=>x+1).map(x=>x*x).map(x=>x-1).map(x=>x*(-1)).map(x=>"The answer is: " + x)

Page 40: From Java to Scala - advantages and possible risks

Mutable collections

● Array Buffers● List Buffers● StringBuilders● Linked Lists● Double Linked Lists● Mutable Lists● Queues● Array Sequences● Stacks● Array Stacks● Hash Tables● Weak Hash Maps● Concurrent Maps● Mutable Bitsets

Page 41: From Java to Scala - advantages and possible risks

Type parametrization - 1 class Queue[T](

private val leading: List[T],

private val trailing: List[T]

) {

private def mirror =

if (leading.isEmpty)

new Queue(trailing.reverse, Nil)

else

this

def head = mirror.leading.head

def tail = {

val q = mirror

new Queue(q.leading.tail, q.trailing)

}

def enqueue(x: T) =

new Queue(leading, x :: trailing)

}

Page 42: From Java to Scala - advantages and possible risks

Type parametrization - covariance scala> def doesNotCompile(q: Queue) {}

<console>:5: error: trait Queue takes type parameters

def doesNotCompile(q: Queue) {}

ˆ

scala> def doesCompile(q: Queue[AnyRef]) {}

doesCompile: (Queue[AnyRef])Unit

trait Queue[+T] { ... }

trait Queue[-T] { ... }

// if a generic parameter type appears as the type of a method parameter,

// the containing class or trait may not be covariant in that type parameter

class Queue[+T] {

def enqueue(x: T) = ???

}

Page 43: From Java to Scala - advantages and possible risks

Contravariance and lower bounds

trait OutputChannel[-T] {

def write(x: T)

}

trait Function1[-S,+T] {

def apply(x: S): T

}

Page 44: From Java to Scala - advantages and possible risks

Upper bounds class Animal

class Dog extends Animal

class Puppy extends Dog

object ScalaUpperBoundsTest {

def main(args: Array[String]) {

val animal = new Animal

val dog = new Dog

val puppy = new Puppy

val animalCarer = new AnimalCarer

//animalCarer.display(animal)

animalCarer.display(dog)

animalCarer.display(puppy)

}

}

class AnimalCarer{

def display [T <: Dog](t: T){

println(t)

}

}

Page 45: From Java to Scala - advantages and possible risks

Implicit conversions

val letters = "ABCEDEFG".foldLeft("")((acc, c) => acc + c + " ")

println(letters) // A B C E D E F G

object Predef extends LowPriorityImplicits with DeprecatedPredef {

...

@inline imlicit def augmentString(x: String): StringOps = new StringOps(x)

...

}

Page 46: From Java to Scala - advantages and possible risks

Implicit parameters

class PreferredPrompt(val preference: String)

class PreferredDrink(val preference: String)

object Greeter {

def greet(name: String)(implicit prompt: PreferredPrompt,

drink: PreferredDrink) {

println("Welcome, "+ name +". The system is ready.")

print("But while you work, ")

println("why not enjoy a cup of "+ drink.preference +"?")

println(prompt.preference)

}

}

object JoesPrefs {

implicit val prompt = new PreferredPrompt("Yes, master> ")

implicit val drink = new PreferredDrink("tea")

}

Page 47: From Java to Scala - advantages and possible risks

Type classes - ordering

def f[A : Ordering](a: A, b: A) = if (implicitly[Ordering[A]].lt(a, b)) a else b

def f[A](a: A, b: A)(implicit ord: Ordering[A]) = {

import ord._

if (a < b) a else b

}

Page 48: From Java to Scala - advantages and possible risks

Type classes - JSONcase class FullName(firstName: String, lastName: String, middleName: Option[String] = None)

...

implicit object NameJsonWriter extends JsonFormat[FullName] {

override def write(obj: FullName): JsValue = JsObject(

”firstName” -> JsString(obj.firstName),

”lastName” -> JsString(obj.lastName),

”middleName” -> obj.middleName.map(JsString(_)).getOrElse(JsNull))

override def read(json: JsValue): FullName = {

json.asJsObject.getFields(”firstName”, ”lastName”, ”middleName”) match {

case Seq(JsString(firstName), JsString(lastName), JsString(middleName)) =>

FullName(firstName, lastName, Some(middleName))

case Seq(JsString(firstName), JsString(lastName)) =>

FullName(firstName, lastName)

case _ => throw new DeserializationException("FullName expected")

...

private def anyToAst[T:JsonWriter](any: T): JsValue = any.toJson

private def astToAny[T:JsonReader](ast: JsValue): T = ast.convertTo[T]

}

Page 49: From Java to Scala - advantages and possible risks

For comprehension case class Book(title: String, authors: String*)

val books: List[Book] = List(

Book("Structure and Interpretation of Computer Programs", "Abelson, Harold", "Sussman, Gerald J."),

Book("Principles of Compiler Design", "Aho, Alfred", "Ullman, Jeffrey" ),

Book("Programming in Modula-2", "Wirth, Niklaus"),

Book("Elements of ML Programming", "Ullman, Jeffrey"),

Book("The Java Language Specification", "Gosling, James", "Joy, Bill", "Steele, Guy", "Bracha, Gilad")

)

// Then, to find the titles of all books whose author's last name is "Gosling":

for (b <- books; a <- b.authors

if a startsWith "Gosling")

yield b.title

// Or, to find the titles of all books that have the string "Program" in their title:

for (b <- books if (b.title indexOf "Program") >= 0)

yield b.title

// Or, to find the names of all authors that have written at least two books in the database:

for (b1 <- books; b2 <- books if b1 != b2;

a1 <- b1.authors; a2 <- b2.authors if a1 == a2)

yield a1

Page 50: From Java to Scala - advantages and possible risks

Combinator parser (external DSL)trait ArithmExprParser extends JavaTokenParsers {

sealed abstract class Tree

case class Add(t1: Tree, t2: Tree) extends Tree

case class Sub(t1: Tree, t2: Tree) extends Tree

case class Mul(t1: Tree, t2: Tree) extends Tree

case class Div(t1: Tree, t2: Tree) extends Tree

case class Num(t: Double) extends Tree

def eval(t: Tree): Double = t match {

case Add(t1, t2) => eval(t1)+eval(t2)

case Sub(t1, t2) => eval(t1)-eval(t2)

case Mul(t1, t2) => eval(t1)*eval(t2)

case Div(t1, t2) => eval(t1)/eval(t2)

case Num(t) => t

}

lazy val expr: Parser[Tree] = term ~ rep("[+-]".r ~ term) ^^ {

case t ~ ts => ts.foldLeft(t) {

case (t1, "+" ~ t2) => Add(t1, t2)

case (t1, "-" ~ t2) => Sub(t1, t2)

}

}

lazy val term = factor ~ rep("[*/]".r ~ factor) ^^ {

case t ~ ts => ts.foldLeft(t) {

case (t1, "*" ~ t2) => Mul(t1, t2)

case (t1, "/" ~ t2) => Div(t1, t2)

}

}

lazy val factor = "(" ~> expr <~ ")" | num

lazy val num = floatingPointNumber ^^ { t => Num(t.toDouble) }

}

GET / controllers.Application.index

GET /ws controllers.Application.ws

Page 51: From Java to Scala - advantages and possible risks

Streamscala> List("a", ”b”, ”c”) zip (Stream from 1)

res5: List[(java.lang.String, Int)] = List((a,1), (b,2), (c,3))

scala> val s = 1 #:: {

| println(”HI”)

| 2

| } #:: {

| println("BAI”)

| 3

| } #:: Stream.empty

s: scala.collection.immutable.Stream[Int] = Stream(1, ?)

scala> s(0)

res39: Int = 1

scala> s(1)

HI

res40: Int = 2

scala> s(2)

BAI

res41: Int = 3

scala> s

res43: scala.collection.immutable.Stream[Int] = Stream(1, 2, 3)

Page 52: From Java to Scala - advantages and possible risks

Concurrency – Futures - 1 import ExecutionContext.Implicits.global

val session = SocialNetwork.createSessionFor("user", SocialNetwork.credentials)

val f: Future[List[String]] = Future {

session.getRecentPosts()

}

f onComplete {

case Success(posts) => for (post <- posts) println(post)

case Failure(t) => println("An error has occured: " + t.getMessage)

}

f onSuccess { case posts => for (post <- posts) println(post) }

f onFailure { case t => println("An error has occured: " + t.getMessage) }

@volatile var totalA = 0

val text = Future { "na" * 16 + "BATMAN!!!" }

text onSuccess { case txt => totalA += txt.count(_ == 'a') }

text onSuccess { case txt => totalA += txt.count(_ == 'A') }

Page 53: From Java to Scala - advantages and possible risks

Concurrency – Futures - 2 val rateQuote = Future {

connection.getCurrentValue(USD)

}

{ // Version 1

rateQuote onSuccess { case quote =>

val purchase = Future {

if (isProfitable(quote)) connection.buy(amount, quote)

else throw new Exception("not profitable")

}

purchase onSuccess {

case _ => println("Purchased " + amount + " USD")

}

}

}

{ // Version 2

val purchase = rateQuote map { quote =>

If (isProfitable(quote)) connection.buy(amount, quote)

else throw new Exception("not profitable")

}

purchase onSuccess {

case _ => println("Purchased " + amount + " USD")

}

}

Page 54: From Java to Scala - advantages and possible risks

Concurrency – Futures - 3 {

val usdQuote = Future { connection.getCurrentValue(USD) }

val chfQuote = Future { connection.getCurrentValue(CHF) }

val purchase = for {

usd <- usdQuote

chf <- chfQuote if isProfitable(usd, chf)

} yield connection.buy(amount, chf)

purchase onSuccess { case _ => println("Purchased " + amount + " CHF") }

}

{

val purchase: Future[Int] = rateQuote map {

quote => connection.buy(amount, quote)

} recover {

case QuoteChangedException() => 0

}

}

Page 55: From Java to Scala - advantages and possible risks

Concurrency - Promise import scala.concurrent.ExecutionContext.Implicits.global

val p = Promise[T]()

val f = p.future

val producer = Future {

val r = produceSomething()

if (isInvalid(r))

p failure (new IllegalStateException)

else {

val q = doSomeMoreComputation(r)

p success r

}

continueDoingSomethingUnrelated()

}

val consumer = Future {

startDoingSomething()

f onSuccess { case r => doSomethingWithResult() }

}

Page 56: From Java to Scala - advantages and possible risks

Reactive - 1

● Responsive - it must react to its users ● Resilient - it must react to failure and stay

available● Elastic - it must react to variable load

conditions● Message-driven - it must react to inputs

Page 57: From Java to Scala - advantages and possible risks

Reactive - 2

Page 58: From Java to Scala - advantages and possible risks

Reactive - 3case class ParallelRetrievalExampleScala (val cacheRetriever: CacheRetriever, val dbRetriever: DBRetriever) {

def retrieveCustomer(id: Long) : Future[Customer] = {

val cacheFuture = Future { cacheRetriever.getCustomer(id) }

val dbFuture = Future { dbRetriever.getCustomer(id) }

Future.firstCompletedOf(List(cacheFuture, dbFuture))

}

}

public class ParallelRetrievalExample {

final CacheRetriever cacheRetriever;

final DBRetriever dbRetriever;

ParallelRetrievalExample(CacheRetriever cacheRetriever,

DBRetriever dbRetriever) {

this.cacheRetriever = cacheRetriever;

this.dbRetriever = dbRetriever;

}

public Object retrieveCustomer(final long id) {

final CompletableFuture<Object> cacheFuture = CompletableFuture.supplyAsync(() -> {return cacheRetriever.getCustomer(id); });

final CompletableFuture<Object> dbFuture = CompletableFuture.supplyAsync(() -> { return dbRetriever.getCustomer(id); });

return CompletableFuture.anyOf(cacheFuture, dbFuture);

}

}

Page 59: From Java to Scala - advantages and possible risks

Reactive - 4● def getProductInventoryByPostalCode( productSku: Long, postalCode: String): Future[(Long, Map[Long, Long])] = {

implicit val ec = ExecutionContext.fromExecutor(new ForkJoinPool())

implicit val timeout = 250 milliseconds

val localInventoryFuture = Future {

inventoryService.currentInventoryInWarehouse(

productSku, postalCode)

}

val overallInventoryFutureByWarehouse = Future {

inventoryService.currentInventoryOverallByWarehouse(

productSku)

}

for {

local <- localInventoryFuture

overall <- overallInventoryFutureByWarehouse

} yield (local, overall)

}

Page 60: From Java to Scala - advantages and possible risks

Reactive - 5case object Start

case class CounterMessage(counterValue: Int)

case class CounterTooLargeException(message: String) extends Exception(message)

class SupervisorActor extends Actor with ActorLogging {

override val supervisorStrategy = OneForOneStrategy() { case _: CounterTooLargeException => Restart }

val actor2 = context.actorOf(Props[SecondActor], "second-actor")

val actor1 = context.actorOf(Props(new FirstActor(actor2)), "first-actor")

def receive = { case Start => actor1 ! Start }

}

class AbstractCounterActor extends Actor with ActorLogging {

var counterValue = 0

def receive = { case _ => }

def counterReceive: Receive = LoggingReceive {

case CounterMessage(i) if i < 1000 => counterValue = i

log.info(s"Counter value: $counterValue")

sender ! CounterMessage(counterValue + 1)

case CounterMessage(i) => throw new CounterTooLargeException("Exceeded max value of counter!")

}

override def postRestart(reason: Throwable) = { context.parent ! Start }

}

Page 61: From Java to Scala - advantages and possible risks

Reactive - 6class FirstActor(secondActor: ActorRef) extends

AbstractCounterActor {

override def receive = LoggingReceive {

case Start =>

context.become(counterReceive)

log.info("Starting counter passing.")

secondActor ! CounterMessage(counterValue + 1)

}

}

class SecondActor() extends AbstractCounterActor {

override def receive = counterReceive

}

object Example extends App {

val system = ActorSystem("counter-supervision-example")

val supervisor = system.actorOf(Props[SupervisorActor])

supervisor ! Start

}

Page 62: From Java to Scala - advantages and possible risks

Reactive app – Spray + Slick + Akkaobject ExecutionContexts {

private val contextsSettings = ExecutionContextsSettings()

implicit val dbExecutionContext = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(contextsSettings.blockingThreadCount))

implicit val cpuIntensiveExecutionContext = ExecutionContext.fromExecutor(Executors.newWorkStealingPool(contextsSettings.cpuIntensiveThreadCount))

}

def accountSignUpRoute = {

respondWithMediaType(MediaTypes.`application/json`) {

(path(ApiRoot / "account") & post) {

entity(as[SignUp]) { signUp =>

onSuccess(accountService.createAccount(signUp)) {

case Left(failure) => complete(StatusCodes.Conflict,

"The email address you provided is already registered to another account")

case Right(acc) =>

respondWithHeader(Location(s"/$ApiRoot/account/${acc.id.get}")) {

setSession(SessionCookie(data = Map("id" -> acc.id.get.toString), path = Some("/"))) {

complete(StatusCodes.Created, acc)

}

}

}

}

}

}

}

Page 63: From Java to Scala - advantages and possible risks

Reactive app - 2

def createAccount(account: Account): Future[Either[AccountCreationFailure, Account]] =

Future {

db withTransaction { implicit session =>

opTimer.time {

// check that email address does not already exist

accountRepo.retrieveAccountByEmail(account.email) match {

case Some(acc) => Left(AccountCreationFailure(account.email)) // reject account exists

case _ =>

val createdAcc = accountRepo.createAccount(account)

pictureRepo.createPicture(Picture(url = "http://bit.ly/1y3A9HS", accountId = createdAcc.id.get))

Right(createdAcc)

}

}

}

}

Page 64: From Java to Scala - advantages and possible risks

Reactive app - 3 self.sendChatMessage = function() {

$.ajax("/api/chat",

{data: ko.toJSON({"message": self.newChatMessage})

, type: "post"

, contentType: "application/json"

, headers: { "sessionCsfrToken": $("#sessionCsfrToken").val() }

, error: function(jqXHR, textStatus, errorThrown) {

var json = JSON.parse(jqXHR.responseText);

if(json.redirect) {

window.location = json.redirect;

}

}

}

).always(function() { self.newChatMessage(''); })

};

// event source

self.makeEventSource = function() {

var s = new EventSource("/streaming/chat");

s.addEventListener("message", function(e) {

var parsed = JSON.parse(e.data);

var msg = new ChatMessage(parsed);

self.messages.push(msg);

}, false);

return s;

};

Page 65: From Java to Scala - advantages and possible risks

Reactive app -4

def chatRoute(implicit session: SessionCookie) = {

pathPrefix(ApiRoot) {

(path("chat") & post) {

chatTimer.time {

entity(as[ChatActor.ChatMessage]) { msg =>

chat ! msg

complete(StatusCodes.Accepted)

}

}

}

} ~ (get & pathPrefix("streaming")) {

respondAsEventStream {

path("chat") { ctx =>

chat ! ChatActor.AddListener(ctx)

}

}

}

}

Page 66: From Java to Scala - advantages and possible risks

Reactive app - 5class Chat(implicit inj: Injector) extends Actor with ActorLogging {

import Chat._

log.info("Starting chat actor.")

val watched = ArrayBuffer.empty[ActorRef]

def receive : Receive = {

case AddListener(ctx) =>

log.info(s"Adding SSE listener.")

val listener = context.actorOf(SSEActor.props(ctx))

context.watch(listener)

watched += listener

case msg @ ChatMessage(_) =>

log.info(s"Received chat message.")

watched.foreach(_ ! SSEActor.SSEEvent(event=Some("message"),data=List(msg.toJson.compactPrint)))

case Terminated(listener) =>

watched -= listener

}

}

Page 67: From Java to Scala - advantages and possible risks

Reactive app - 6private[chat]

class SSEActor(ctx:RequestContext) extends Actor with ActorLogging {

import SSEActor._

val comment = ":\n\n"

ctx.responder ! ChunkedResponseStart(HttpResponse(entity = comment))

context.setReceiveTimeout(20.seconds)

def receive: Receive = {

case evt @ SSEEvent(_,_,_,_) =>

log.debug(s"Sending SSE event: ${evt.toString}")

ctx.responder ! MessageChunk(evt.toString)

case ReceiveTimeout =>

ctx.responder ! MessageChunk(comment)

case SSEEnd =>

ctx.responder ! ChunkedMessageEnd

context.stop(self)

case SSEClose =>

// notify client to stop retrying

ctx.responder ! StatusCodes.NotFound

context.stop(self)

case ev: Http.ConnectionClosed =>

log.info(s"Stopping SSE stream, reason: signed out")

context.stop(self)

}

}

Page 68: From Java to Scala - advantages and possible risks

Persistence layer - 1private[account]

class AccountTable(tag: Tag) extends Table[Account](tag, "account") with Mappers {

def id = column[Long]("id", O.PrimaryKey, O.AutoInc)

def name = column[Name]("name", O.NotNull)

def email = column[Email]("email", O.NotNull)

def password = column[Password]("password", O.NotNull)

def activatedAt = column[LocalDateTime]("activated_at", O.Nullable)

def suspendedAt = column[LocalDateTime]("suspended_at", O.Nullable)

def * =

(id.?, name, email, password,

activatedAt.?, suspendedAt.?) <>

((Account.apply _).tupled, Account.unapply)

}

Page 69: From Java to Scala - advantages and possible risks

Persistence layer - 2private[account]

class Accounts extends MetricsInstrumented with Mappers {

implicit val dbExecContext = ExecutionContexts.dbExecutionContext

import storage.operationSuccessMapper

import scala.slick.jdbc.JdbcBackend.Session

private[this] val logger = Logger[this.type]

private val siteSettings = SiteSettings()

val accounts = TableQuery[AccountTable]

private val qRetrieveAccountByEmail = Compiled( (email: Column[Email]) =>

for { account <- accounts if account.email === email } yield account)

private val qRetrieveAccountPassword = Compiled( (id: Column[Long]) =>

for { account <- accounts if account.id === id } yield account.password )

private val qRetrieveAccount = Compiled( (id: Column[Long]) =>

for { account <- accounts if account.id === id } yield account ) // or accounts.filter(_.id === account.id.get)

def createAccount(account: Account)(implicit session: Session): Account = {

logger.info("account created")

val pw = Password.encrypt(siteSettings.encryptionLogRounds)(account.password)

// set random values

val createdAt = account.createdAt.getOrElse(LocalDateTime.now)

val currentLoginAt, lastLoginAt = LocalDateTime.now

val acc: Account = account.copy(password = pw, createdAt = Some(createdAt))

val id = accounts.returning(accounts.map(_.id)) += acc

account.copy(id = Some(id))

}

def retrieveAccount(id: Long)(implicit session: Session): Option[Account] = {

logger.info("account retrieved1")

qRetrieveAccount(id).firstOption

}

}

Page 70: From Java to Scala - advantages and possible risks

Akka persistence

● PersistentActor - a persistent, stateful actor. It is able to persist events to a journal and can react to them in a thread-safe manner. It can be used to implement both command as well as event sourced actors

● PersistentView - a view is a persistent, stateful actor that receives journaled messages that have been written by another persistent actor. A view itself does not journal new messages, instead, it updates internal state only from a persistent actor's replicated message stream.

● AtLeastOnceDelivery - sends messages with at-least-once delivery semantics to destinations, also in case of sender and receiver JVM crashes.

● AsyncWriteJournal - a journal stores the sequence of messages sent to a persistent actor. An application can control which messages are journaled and which are received by the persistent actor without being journaled. The storage backend of a journal is pluggable.

● Snapshot store - A snapshot store persists snapshots of a persistent actor's or a view's internal state. Snapshots are used for optimizing recovery times. The storage backend of a snapshot store is pluggable.

Page 71: From Java to Scala - advantages and possible risks

Akka persistence (PersistentActor)class ExamplePersistentActor extends PersistentActor {

override def persistenceId = "sample-id-1"

var state = ExampleState()

def updateState(event: Evt): Unit =

state = state.updated(event)

def numEvents =

state.size

val receiveRecover: Receive = {

case evt: Evt => updateState(evt)

case SnapshotOffer(_, snapshot: ExampleState) => state = snapshot

}

val receiveCommand: Receive = {

case Cmd(data) =>

persist(Evt(s"${data}-${numEvents}"))(updateState)

persist(Evt(s"${data}-${numEvents + 1}")) { event =>

updateState(event)

context.system.eventStream.publish(event)

}

case "snap" => saveSnapshot(state)

case "print" => println(state)

}

}

Page 72: From Java to Scala - advantages and possible risks

Persistent actor failure example

class ExamplePersistentActor extends PersistentActor {

override def persistenceId = "sample-id-2"

var received: List[String] = Nil // state

def receiveCommand: Receive = {

case "print" => println(s"received ${received.reverse}")

case "boom" => throw new Exception("boom")

case payload: String =>

persist(payload) { p => received = p :: received }

}

def receiveRecover: Receive = {

case s: String => received = s :: received

}

}

Page 73: From Java to Scala - advantages and possible risks

Persistent view● class ExampleView extends PersistentView {

private var numReplicated = 0

override def persistenceId: String = "sample-id-4"

override def viewId = "sample-view-id-4"

def receive = {

case "snap" =>

println(s"view saving snapshot")

saveSnapshot(numReplicated)

case SnapshotOffer(metadata, snapshot: Int) =>

numReplicated = snapshot

println(s"view received snapshot offer ${snapshot} (metadata = ${metadata})")

case payload if isPersistent =>

numReplicated += 1

println(s"view replayed event ${payload} (num replicated = ${numReplicated})")

case SaveSnapshotSuccess(metadata) =>

println(s"view saved snapshot (metadata = ${metadata})")

case SaveSnapshotFailure(metadata, reason) =>

println(s"view snapshot failure (metadata = ${metadata}), caused by ${reason}")

case payload =>

println(s"view received other message ${payload}")

}

}

Page 74: From Java to Scala - advantages and possible risks

Lazy valsscala> object Demo {

val x = { println("initializing x"); "done" }

}

scala> Demo

initializing x

res3: Demo.type = Demo$@17469af

scala> Demo.x

res4: java.lang.String = done

scala> object Demo {

lazy val x = { println("initializing x"); "done" }

}

scala> Demo

res5: Demo.type = Demo$@11dda2d

scala> Demo.x

initializing x

res6: java.lang.String = done

Page 75: From Java to Scala - advantages and possible risks

ScalaTest – testing specs styles - 1import org.scalatest.FunSuite

class SetSuite extends FunSuite {

test("An empty Set should have size 0") {

assert(Set.empty.size == 0)

}

test("Invoking head on an empty Set should produce NoSuchElementException") {

intercept[NoSuchElementException] {

Set.empty.head

}

}

}

import org.scalatest.FlatSpec

class SetSpec extends FlatSpec {

"An empty Set" should "have size 0" in {

assert(Set.empty.size == 0)

}

it should "produce NoSuchElementException when head is invoked" in {

intercept[NoSuchElementException] {

Set.empty.head

}

}

}

Page 76: From Java to Scala - advantages and possible risks

ScalaTest – testing specs styles - 2import org.scalatest.FunSpec

class SetSpec extends FunSpec {

describe("A Set") {

describe("when empty") {

it("should have size 0") {

assert(Set.empty.size == 0)

}

it("should produce NoSuchElementException when head is invoked") {

intercept[NoSuchElementException] {

Set.empty.head

}

}

}

}

}

import org.scalatest.WordSpec

class SetSpec extends WordSpec {

"A Set" when {

"empty" should {

"have size 0" in {

assert(Set.empty.size == 0)

}

"produce NoSuchElementException when head is invoked" in {

intercept[NoSuchElementException] {

Set.empty.head

}

}

}

}

}

Page 77: From Java to Scala - advantages and possible risks

Testing with mock objects

● ScalaMock● EasyMock● JMock● Mockito

Page 78: From Java to Scala - advantages and possible risks

Testing with mock objects - ScalaMock

class ExampleSpec extends FlatSpec with MockFactory {

...

// Function mocks

val m = mockFunction[Int, String]

m expects ( 42) returning "Forty two" once

// Proxy mocks

val m = mock[Turtle]

m expects 'setPosition withArgs(10.0, 10.0)

m expects 'forward withArgs (5.0)

m expects 'getPosition returning(15.0, 10.0)

m expects 'forward withArgs(*) once

m expects 'forward

m expects 'forward anyNumberOfTimes

m stubs 'forward

...

}

Page 79: From Java to Scala - advantages and possible risks

Property-based testingclass Fraction(n: Int, d: Int) {

require(d != 0)

require(d != Integer.MIN_VALUE)

require(n != Integer.MIN_VALUE)

val numer = if (d < 0) -1 * n else n

val denom = d.abs

override def toString = numer + " / " + denom

}

forAll { (n: Int, d: Int) =>

whenever (d != 0 && d != Integer.MIN_VALUE

&& n != Integer.MIN_VALUE) {

val f = new Fraction(n, d)

if (n < 0 && d < 0 || n > 0 && d > 0)

f.numer should be > 0

else if (n != 0)

f.numer should be < 0

else

f.numer should be === 0

f.denom should be > 0

}

}