SlideShare a Scribd company logo
From Java to Scala - advantages
and possible risks
Oleksii Petinov
Scala engineer,
Newground
Scala = “scalable”
● Scalable = extensible
small 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
Who is using Scala
● Twitter
● Foursquare
● UK Guardian
● Sony Pictures Entertainment
● SAP
● Xerox
● Novell
● Netflix
● Tumblr (partially)
Compatibility and learning
● Very good Java interop
● Easy to get into – allows (almost) Java style
programming
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)
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)
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()
}
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))
}
}
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)))
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 <)))
}
}
High level – control structures
Definition (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)
}
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)
)
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"
}
Inheritance “diamond” problem
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)
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)
}
Function literals and values
● Function literals and values
scala> val increase = (x: Int) => x + 1
increase: (Int) => Int = <function1>
scala> increase(10)
res0: Int = 11
● Function literals as predicates
someNumbers.filter(x => x > 0)
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())
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
}
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)
}
Partially applied functions
Original 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
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)
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
}
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(_))
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
Scala types (everything is object)
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
}
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) =>
}
Dependency Injection
● Non-framework DI Patterns (constructor,
setter, cake, thin-cake, implicits,
scalaz.Reader monad)
● DI frameworks (spring, guice, Subcut,
MacWire, Scaldi)
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)
}
}
}
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))
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))
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
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))
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
Scala collections: uniformity
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
}
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)
}
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)
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
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)
}
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) = ???
}
Contravariance and lower bounds
trait OutputChannel[-T] {
def write(x: T)
}
trait Function1[-S,+T] {
def apply(x: S): T
}
Upper boundsclass 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)
}
}
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)
...
}
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")
}
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
}
Type classes - JSON
case 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]
}
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
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
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)
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') }
Concurrency – Futures - 2val 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")
}
}
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
}
}
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() }
}
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
Reactive - 2
Reactive - 3
case 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);
}
}
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)
}
Reactive - 5
case 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 }
}
Reactive - 6
class 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
}
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)
}
}
}
}
}
}
}
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)
}
}
}
}
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;
};
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)
}
}
}
}
Reactive app - 5
class 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
}
}
Reactive app - 6private[chat]
class SSEActor(ctx:RequestContext) extends Actor with ActorLogging {
import SSEActor._
val comment = ":nn"
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)
}
}
Persistence layer - 1
private[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)
}
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
}
}
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.
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)
}
}
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
}
}
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}")
}
}
Lazy vals
scala> 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
ScalaTest – testing specs styles - 1
import 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
}
}
}
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
}
}
}
}
}
Testing with mock objects
● ScalaMock
● EasyMock
● JMock
● Mockito
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
...
}
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
}
}

More Related Content

KEY
ddd+scala
PPT
SDC - Einführung in Scala
ODP
1.2 scala basics
PDF
여자개발자모임터 6주년 개발 세미나 - Scala Language
PDF
Meet scala
PDF
Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébasti...
PDF
Swift for TensorFlow - CoreML Personalization
PDF
Scala vs Java 8 in a Java 8 World
ddd+scala
SDC - Einführung in Scala
1.2 scala basics
여자개발자모임터 6주년 개발 세미나 - Scala Language
Meet scala
Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébasti...
Swift for TensorFlow - CoreML Personalization
Scala vs Java 8 in a Java 8 World

What's hot (17)

PDF
A bit about Scala
PPTX
Joy of scala
PDF
ハイブリッド言語Scalaを使う
PDF
Starting with Scala : Frontier Developer's Meetup December 2010
PDF
Scala for Jedi
PDF
Scala collections
PPTX
Scala Back to Basics: Type Classes
PDF
FunScript 2013 (with speakers notes)
PDF
响应式编程及框架
PDF
Pragmatic Real-World Scala (short version)
PDF
Grammarware Memes
PPTX
All about scala
PDF
Introduction to Python
ODP
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...
ODP
JavaScript Web Development
PDF
Scala in Places API
PDF
scalaliftoff2009.pdf
A bit about Scala
Joy of scala
ハイブリッド言語Scalaを使う
Starting with Scala : Frontier Developer's Meetup December 2010
Scala for Jedi
Scala collections
Scala Back to Basics: Type Classes
FunScript 2013 (with speakers notes)
响应式编程及框架
Pragmatic Real-World Scala (short version)
Grammarware Memes
All about scala
Introduction to Python
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...
JavaScript Web Development
Scala in Places API
scalaliftoff2009.pdf
Ad

Viewers also liked (20)

PDF
Scala for Java Developers (Silicon Valley Code Camp 13)
PDF
Scala dreaded underscore
PPTX
FCWDS (Foreign Construction Workers Directory System) Introductory Presentation
PPTX
On the development of beliefs vs. capacities: A post-metaphysical view of sec...
PPT
Reading the Campus/Reading the City
DOCX
Sekolah kebangsaan jalan raja syed alwi
PDF
article198
PPT
Why scala - executive overview
PPT
3cork and kerry
PPT
HealthCare BPO
PPTX
sdfghjk
PPTX
1960 1969
PPT
The new masters of management
PDF
My SQL Idiosyncrasies That Bite OTN
PDF
Product Development Communications Executive Pat Scherschel 2011
PPTX
MBA study material- Ethics
PDF
Simple c-programs
PPTX
Poríferos e cnidários 3C- 2015
PPT
RK Swamy BBDO Pre-Summers
PDF
Sovereignty, Free Will, and Salvation - Limited Atonement
Scala for Java Developers (Silicon Valley Code Camp 13)
Scala dreaded underscore
FCWDS (Foreign Construction Workers Directory System) Introductory Presentation
On the development of beliefs vs. capacities: A post-metaphysical view of sec...
Reading the Campus/Reading the City
Sekolah kebangsaan jalan raja syed alwi
article198
Why scala - executive overview
3cork and kerry
HealthCare BPO
sdfghjk
1960 1969
The new masters of management
My SQL Idiosyncrasies That Bite OTN
Product Development Communications Executive Pat Scherschel 2011
MBA study material- Ethics
Simple c-programs
Poríferos e cnidários 3C- 2015
RK Swamy BBDO Pre-Summers
Sovereignty, Free Will, and Salvation - Limited Atonement
Ad

Similar to From Java to Scala - advantages and possible risks (20)

PDF
Coding in Style
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
PDF
(How) can we benefit from adopting scala?
PDF
Spark workshop
ODP
PDF
Scala coated JVM
PPT
Scala - brief intro
PDF
The Scala Programming Language
PPTX
Scala for curious
ODP
Functional programming with Scala
PDF
Monadologie
PDF
Scala @ TechMeetup Edinburgh
PDF
Introduction to Scala
PDF
Hadoop + Clojure
PPTX
Principles of functional progrmming in scala
PDF
Hw09 Hadoop + Clojure
PPT
An introduction to scala
Coding in Style
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
(How) can we benefit from adopting scala?
Spark workshop
Scala coated JVM
Scala - brief intro
The Scala Programming Language
Scala for curious
Functional programming with Scala
Monadologie
Scala @ TechMeetup Edinburgh
Introduction to Scala
Hadoop + Clojure
Principles of functional progrmming in scala
Hw09 Hadoop + Clojure
An introduction to scala

More from SeniorDevOnly (6)

PPTX
Viacheslav Rudenko - Case Study: How 4 university friends battled US Mobile M...
PPTX
Ievgenii Narovlianskyi - Ruby is not just a gem
PPTX
Getting rid of pain with Heroku @ BrainDev Kyiv
PPTX
Why I don’t want to develop iOS apps in Objective C
PDF
Parse com alternatives
ODP
Viacheslav Rudenko - Case Study: How 4 university friends battled US Mobile M...
Ievgenii Narovlianskyi - Ruby is not just a gem
Getting rid of pain with Heroku @ BrainDev Kyiv
Why I don’t want to develop iOS apps in Objective C
Parse com alternatives

Recently uploaded (20)

PDF
Optimise Shopper Experiences with a Strong Data Estate.pdf
PPTX
Managing Community Partner Relationships
PPTX
oil_refinery_comprehensive_20250804084928 (1).pptx
PPTX
Database Infoormation System (DBIS).pptx
PPT
Predictive modeling basics in data cleaning process
PPTX
STERILIZATION AND DISINFECTION-1.ppthhhbx
PPTX
IBA_Chapter_11_Slides_Final_Accessible.pptx
PDF
Introduction to Data Science and Data Analysis
PDF
BF and FI - Blockchain, fintech and Financial Innovation Lesson 2.pdf
PPTX
Introduction to Firewall Analytics - Interfirewall and Transfirewall.pptx
PPT
ISS -ESG Data flows What is ESG and HowHow
PPTX
Acceptance and paychological effects of mandatory extra coach I classes.pptx
PDF
[EN] Industrial Machine Downtime Prediction
PPTX
01_intro xxxxxxxxxxfffffffffffaaaaaaaaaaafg
PPTX
Introduction-to-Cloud-ComputingFinal.pptx
PPTX
SAP 2 completion done . PRESENTATION.pptx
PDF
annual-report-2024-2025 original latest.
PDF
Clinical guidelines as a resource for EBP(1).pdf
PPTX
IB Computer Science - Internal Assessment.pptx
PDF
Mega Projects Data Mega Projects Data
Optimise Shopper Experiences with a Strong Data Estate.pdf
Managing Community Partner Relationships
oil_refinery_comprehensive_20250804084928 (1).pptx
Database Infoormation System (DBIS).pptx
Predictive modeling basics in data cleaning process
STERILIZATION AND DISINFECTION-1.ppthhhbx
IBA_Chapter_11_Slides_Final_Accessible.pptx
Introduction to Data Science and Data Analysis
BF and FI - Blockchain, fintech and Financial Innovation Lesson 2.pdf
Introduction to Firewall Analytics - Interfirewall and Transfirewall.pptx
ISS -ESG Data flows What is ESG and HowHow
Acceptance and paychological effects of mandatory extra coach I classes.pptx
[EN] Industrial Machine Downtime Prediction
01_intro xxxxxxxxxxfffffffffffaaaaaaaaaaafg
Introduction-to-Cloud-ComputingFinal.pptx
SAP 2 completion done . PRESENTATION.pptx
annual-report-2024-2025 original latest.
Clinical guidelines as a resource for EBP(1).pdf
IB Computer Science - Internal Assessment.pptx
Mega Projects Data Mega Projects Data

From Java to Scala - advantages and possible risks

  • 1. From Java to Scala - advantages and possible risks Oleksii Petinov Scala engineer, Newground
  • 2. Scala = “scalable” ● Scalable = extensible small 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
  • 3. Who is using Scala ● Twitter ● Foursquare ● UK Guardian ● Sony Pictures Entertainment ● SAP ● Xerox ● Novell ● Netflix ● Tumblr (partially)
  • 4. Compatibility and learning ● Very good Java interop ● Easy to get into – allows (almost) Java style programming
  • 5. 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)
  • 6. 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)
  • 7. 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() }
  • 8. 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)) } }
  • 9. 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)))
  • 10. 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 <))) } }
  • 11. High level – control structures Definition (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) }
  • 12. 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) )
  • 13. 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" }
  • 15. 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)
  • 16. 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) }
  • 17. Function literals and values ● Function literals and values scala> val increase = (x: Int) => x + 1 increase: (Int) => Int = <function1> scala> increase(10) res0: Int = 11 ● Function literals as predicates someNumbers.filter(x => x > 0)
  • 18. 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())
  • 19. 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 }
  • 20. 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) }
  • 21. Partially applied functions Original 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
  • 22. 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)
  • 23. 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 }
  • 24. 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(_))
  • 25. 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
  • 27. 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 }
  • 28. 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) => }
  • 29. Dependency Injection ● Non-framework DI Patterns (constructor, setter, cake, thin-cake, implicits, scalaz.Reader monad) ● DI frameworks (spring, guice, Subcut, MacWire, Scaldi)
  • 30. 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) } } }
  • 31. 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))
  • 32. 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))
  • 33. 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
  • 34. 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))
  • 35. 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
  • 37. 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 }
  • 38. 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) }
  • 39. 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)
  • 40. 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
  • 41. 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) }
  • 42. 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) = ??? }
  • 43. Contravariance and lower bounds trait OutputChannel[-T] { def write(x: T) } trait Function1[-S,+T] { def apply(x: S): T }
  • 44. Upper boundsclass 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) } }
  • 45. 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) ... }
  • 46. 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") }
  • 47. 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 }
  • 48. Type classes - JSON case 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] }
  • 49. 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
  • 50. 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
  • 51. 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)
  • 52. 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') }
  • 53. Concurrency – Futures - 2val 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") } }
  • 54. 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 } }
  • 55. 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() } }
  • 56. 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
  • 58. Reactive - 3 case 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); } }
  • 59. 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) }
  • 60. Reactive - 5 case 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 } }
  • 61. Reactive - 6 class 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 }
  • 62. 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) } } } } } } }
  • 63. 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) } } } }
  • 64. 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; };
  • 65. 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) } } } }
  • 66. Reactive app - 5 class 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 } }
  • 67. Reactive app - 6private[chat] class SSEActor(ctx:RequestContext) extends Actor with ActorLogging { import SSEActor._ val comment = ":nn" 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) } }
  • 68. Persistence layer - 1 private[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) }
  • 69. 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 } }
  • 70. 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.
  • 71. 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) } }
  • 72. 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 } }
  • 73. 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}") } }
  • 74. Lazy vals scala> 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
  • 75. ScalaTest – testing specs styles - 1 import 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 } } }
  • 76. 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 } } } } }
  • 77. Testing with mock objects ● ScalaMock ● EasyMock ● JMock ● Mockito
  • 78. 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 ... }
  • 79. 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 } }