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
Christian Baranowski
 
ODP
1.2 scala basics
futurespective
 
PDF
여자개발자모임터 6주년 개발 세미나 - Scala Language
Ashal aka JOKER
 
PDF
Meet scala
Wojciech Pituła
 
PDF
Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébasti...
scalaconfjp
 
PDF
Swift for TensorFlow - CoreML Personalization
Jacopo Mangiavacchi
 
PDF
Scala vs Java 8 in a Java 8 World
BTI360
 
ddd+scala
潤一 加藤
 
SDC - Einführung in Scala
Christian Baranowski
 
1.2 scala basics
futurespective
 
여자개발자모임터 6주년 개발 세미나 - Scala Language
Ashal aka JOKER
 
Meet scala
Wojciech Pituła
 
Functional Object-Oriented Imperative Scala / 関数型オブジェクト指向命令型 Scala by Sébasti...
scalaconfjp
 
Swift for TensorFlow - CoreML Personalization
Jacopo Mangiavacchi
 
Scala vs Java 8 in a Java 8 World
BTI360
 

What's hot (17)

PDF
A bit about Scala
Vladimir Parfinenko
 
PPTX
Joy of scala
Maxim Novak
 
PDF
ハイブリッド言語Scalaを使う
bpstudy
 
PDF
Starting with Scala : Frontier Developer's Meetup December 2010
Derek Chen-Becker
 
PDF
Scala for Jedi
Vladimir Parfinenko
 
PDF
Scala collections
Inphina Technologies
 
PPTX
Scala Back to Basics: Type Classes
Tomer Gabel
 
PDF
FunScript 2013 (with speakers notes)
Zach Bray
 
PDF
响应式编程及框架
jeffz
 
PDF
Pragmatic Real-World Scala (short version)
Jonas Bonér
 
PDF
Grammarware Memes
Eelco Visser
 
PPTX
All about scala
Yardena Meymann
 
PDF
Introduction to Python
UC San Diego
 
ODP
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...
Sanjeev_Knoldus
 
ODP
JavaScript Web Development
vito jeng
 
PDF
Scala in Places API
Łukasz Bałamut
 
PDF
scalaliftoff2009.pdf
Hiroshi Ono
 
A bit about Scala
Vladimir Parfinenko
 
Joy of scala
Maxim Novak
 
ハイブリッド言語Scalaを使う
bpstudy
 
Starting with Scala : Frontier Developer's Meetup December 2010
Derek Chen-Becker
 
Scala for Jedi
Vladimir Parfinenko
 
Scala collections
Inphina Technologies
 
Scala Back to Basics: Type Classes
Tomer Gabel
 
FunScript 2013 (with speakers notes)
Zach Bray
 
响应式编程及框架
jeffz
 
Pragmatic Real-World Scala (short version)
Jonas Bonér
 
Grammarware Memes
Eelco Visser
 
All about scala
Yardena Meymann
 
Introduction to Python
UC San Diego
 
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...
Sanjeev_Knoldus
 
JavaScript Web Development
vito jeng
 
Scala in Places API
Łukasz Bałamut
 
scalaliftoff2009.pdf
Hiroshi Ono
 
Ad

Viewers also liked (20)

PDF
Scala for Java Developers (Silicon Valley Code Camp 13)
Ramnivas Laddad
 
PDF
Scala dreaded underscore
RUDDER
 
PPTX
FCWDS (Foreign Construction Workers Directory System) Introductory Presentation
Kelvin Koh
 
PPTX
On the development of beliefs vs. capacities: A post-metaphysical view of sec...
perspegrity5
 
PPT
Reading the Campus/Reading the City
Tina Richardson
 
DOCX
Sekolah kebangsaan jalan raja syed alwi
Norshida Shida
 
PDF
article198
nonchalantfurvo01
 
PPT
Why scala - executive overview
Razvan Cojocaru
 
PPT
3cork and kerry
riaenglish
 
PPT
HealthCare BPO
procurementservices
 
PPTX
sdfghjk
robcar1986
 
PPTX
1960 1969
Rachel Inbar
 
PPT
The new masters of management
rsoosaar
 
PDF
My SQL Idiosyncrasies That Bite OTN
Ronald Bradford
 
PDF
Product Development Communications Executive Pat Scherschel 2011
patriciaindy2011
 
PPTX
MBA study material- Ethics
Saranya Dhanesh Kumar
 
PDF
Simple c-programs
rashmi322
 
PPTX
Poríferos e cnidários 3C- 2015
Maria Teresa Iannaco Grego
 
PPT
RK Swamy BBDO Pre-Summers
shash4rao
 
PDF
Sovereignty, Free Will, and Salvation - Limited Atonement
Robin Schumacher
 
Scala for Java Developers (Silicon Valley Code Camp 13)
Ramnivas Laddad
 
Scala dreaded underscore
RUDDER
 
FCWDS (Foreign Construction Workers Directory System) Introductory Presentation
Kelvin Koh
 
On the development of beliefs vs. capacities: A post-metaphysical view of sec...
perspegrity5
 
Reading the Campus/Reading the City
Tina Richardson
 
Sekolah kebangsaan jalan raja syed alwi
Norshida Shida
 
article198
nonchalantfurvo01
 
Why scala - executive overview
Razvan Cojocaru
 
3cork and kerry
riaenglish
 
HealthCare BPO
procurementservices
 
sdfghjk
robcar1986
 
1960 1969
Rachel Inbar
 
The new masters of management
rsoosaar
 
My SQL Idiosyncrasies That Bite OTN
Ronald Bradford
 
Product Development Communications Executive Pat Scherschel 2011
patriciaindy2011
 
MBA study material- Ethics
Saranya Dhanesh Kumar
 
Simple c-programs
rashmi322
 
Poríferos e cnidários 3C- 2015
Maria Teresa Iannaco Grego
 
RK Swamy BBDO Pre-Summers
shash4rao
 
Sovereignty, Free Will, and Salvation - Limited Atonement
Robin Schumacher
 
Ad

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

PDF
(How) can we benefit from adopting scala?
Tomasz Wrobel
 
ODP
Scala ntnu
Alf Kristian Støyle
 
PDF
Scala - core features
Łukasz Wójcik
 
PPTX
Softshake 2013: 10 reasons why java developers are jealous of Scala developers
Matthew Farwell
 
PDF
Railroading into Scala
Nehal Shah
 
PDF
Scala: Object-Oriented Meets Functional, by Iulian Dragos
3Pillar Global
 
PPTX
Scala training workshop 02
Nguyen Tuan
 
PDF
Scala or functional programming from a python developer's perspective
gabalese
 
PPT
An introduction to scala
Mohsen Zainalpour
 
PPT
Scala Talk at FOSDEM 2009
Martin Odersky
 
ODP
Functional programming with Scala
Neelkanth Sachdeva
 
PDF
Getting Started With Scala
Meetu Maltiar
 
PDF
Getting Started With Scala
Xebia IT Architects
 
PDF
Scala Intro
Paolo Platter
 
PDF
Introduction To Scala
Innar Made
 
PDF
Introduction to Scala
Aleksandar Prokopec
 
PDF
Programming in scala - 1
Mukesh Kumar
 
PDF
scalaliftoff2009.pdf
Hiroshi Ono
 
PDF
scalaliftoff2009.pdf
Hiroshi Ono
 
PDF
scalaliftoff2009.pdf
Hiroshi Ono
 
(How) can we benefit from adopting scala?
Tomasz Wrobel
 
Scala - core features
Łukasz Wójcik
 
Softshake 2013: 10 reasons why java developers are jealous of Scala developers
Matthew Farwell
 
Railroading into Scala
Nehal Shah
 
Scala: Object-Oriented Meets Functional, by Iulian Dragos
3Pillar Global
 
Scala training workshop 02
Nguyen Tuan
 
Scala or functional programming from a python developer's perspective
gabalese
 
An introduction to scala
Mohsen Zainalpour
 
Scala Talk at FOSDEM 2009
Martin Odersky
 
Functional programming with Scala
Neelkanth Sachdeva
 
Getting Started With Scala
Meetu Maltiar
 
Getting Started With Scala
Xebia IT Architects
 
Scala Intro
Paolo Platter
 
Introduction To Scala
Innar Made
 
Introduction to Scala
Aleksandar Prokopec
 
Programming in scala - 1
Mukesh Kumar
 
scalaliftoff2009.pdf
Hiroshi Ono
 
scalaliftoff2009.pdf
Hiroshi Ono
 
scalaliftoff2009.pdf
Hiroshi Ono
 

More from SeniorDevOnly (6)

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

Recently uploaded (20)

PDF
A Systems Thinking Approach to Algorithmic Fairness.pdf
Epistamai
 
PDF
oop_java (1) of ice or cse or eee ic.pdf
sabiquntoufiqlabonno
 
PPTX
artificial intelligence deeplearning-200712115616.pptx
revathi148366
 
PDF
CH2-MODEL-SETUP-v2017.1-JC-APR27-2017.pdf
jcc00023con
 
PDF
TIC ACTIVIDAD 1geeeeeeeeeeeeeeeeeeeeeeeeeeeeeer3.pdf
Thais Ruiz
 
PPTX
Machine Learning Solution for Power Grid Cybersecurity with GraphWavelets
Sione Palu
 
PDF
Taxes Foundatisdcsdcsdon Certificate.pdf
PratyushPrem2
 
PPTX
International-health-agency and it's work.pptx
shreehareeshgs
 
PPTX
Presentation (1) (1).pptx k8hhfftuiiigff
karthikjagath2005
 
PDF
Data Analyst Certificate Programs for Beginners | IABAC
Seenivasan
 
PDF
Digital Infrastructure – Powering the Connected Age
Heera Yadav
 
PDF
TCP_IP for Programmers ------ slides.pdf
Souhailsouhail5
 
PPTX
Presentation1.pptxvhhh. H ycycyyccycycvvv
ItratBatool16
 
PPTX
batch data Retailer Data management Project.pptx
sumitmundhe77
 
PPTX
Bharatiya Antariksh Hackathon 2025 Idea Submission PPT.pptx
abhinavmemories2026
 
PPTX
Lecture 1 Intro in Inferential Statistics.pptx
MiraLamuton
 
PPTX
Complete_STATA_Introduction_Beginner.pptx
mbayekebe
 
PPTX
Extract Transformation Load (3) (1).pptx
revathi148366
 
PPTX
Introduction to Biostatistics Presentation.pptx
AtemJoshua
 
PPTX
Economic Sector Performance Recovery.pptx
yulisbaso2020
 
A Systems Thinking Approach to Algorithmic Fairness.pdf
Epistamai
 
oop_java (1) of ice or cse or eee ic.pdf
sabiquntoufiqlabonno
 
artificial intelligence deeplearning-200712115616.pptx
revathi148366
 
CH2-MODEL-SETUP-v2017.1-JC-APR27-2017.pdf
jcc00023con
 
TIC ACTIVIDAD 1geeeeeeeeeeeeeeeeeeeeeeeeeeeeeer3.pdf
Thais Ruiz
 
Machine Learning Solution for Power Grid Cybersecurity with GraphWavelets
Sione Palu
 
Taxes Foundatisdcsdcsdon Certificate.pdf
PratyushPrem2
 
International-health-agency and it's work.pptx
shreehareeshgs
 
Presentation (1) (1).pptx k8hhfftuiiigff
karthikjagath2005
 
Data Analyst Certificate Programs for Beginners | IABAC
Seenivasan
 
Digital Infrastructure – Powering the Connected Age
Heera Yadav
 
TCP_IP for Programmers ------ slides.pdf
Souhailsouhail5
 
Presentation1.pptxvhhh. H ycycyyccycycvvv
ItratBatool16
 
batch data Retailer Data management Project.pptx
sumitmundhe77
 
Bharatiya Antariksh Hackathon 2025 Idea Submission PPT.pptx
abhinavmemories2026
 
Lecture 1 Intro in Inferential Statistics.pptx
MiraLamuton
 
Complete_STATA_Introduction_Beginner.pptx
mbayekebe
 
Extract Transformation Load (3) (1).pptx
revathi148366
 
Introduction to Biostatistics Presentation.pptx
AtemJoshua
 
Economic Sector Performance Recovery.pptx
yulisbaso2020
 

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