SlideShare uma empresa Scribd logo
1 de 79
Baixar para ler offline
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
}
}

Mais conteúdo relacionado

Mais procurados

ハイブリッド言語Scalaを使う
ハイブリッド言語Scalaを使うハイブリッド言語Scalaを使う
ハイブリッド言語Scalaを使うbpstudy
 
Starting with Scala : Frontier Developer's Meetup December 2010
Starting with Scala : Frontier Developer's Meetup December 2010Starting with Scala : Frontier Developer's Meetup December 2010
Starting with Scala : Frontier Developer's Meetup December 2010Derek Chen-Becker
 
Scala Back to Basics: Type Classes
Scala Back to Basics: Type ClassesScala Back to Basics: Type Classes
Scala Back to Basics: Type ClassesTomer Gabel
 
FunScript 2013 (with speakers notes)
FunScript 2013 (with speakers notes)FunScript 2013 (with speakers notes)
FunScript 2013 (with speakers notes)Zach Bray
 
响应式编程及框架
响应式编程及框架响应式编程及框架
响应式编程及框架jeffz
 
Pragmatic Real-World Scala (short version)
Pragmatic Real-World Scala (short version)Pragmatic Real-World Scala (short version)
Pragmatic Real-World Scala (short version)Jonas Bonér
 
Introduction to Python
Introduction to PythonIntroduction to Python
Introduction to PythonUC San Diego
 
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...
Scala traits training by Sanjeev Kumar @Kick Start Scala traits & Play, organ...Sanjeev_Knoldus
 
JavaScript Web Development
JavaScript Web DevelopmentJavaScript Web Development
JavaScript Web Developmentvito jeng
 
scalaliftoff2009.pdf
scalaliftoff2009.pdfscalaliftoff2009.pdf
scalaliftoff2009.pdfHiroshi Ono
 

Mais procurados (17)

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

Destaque

Scala for Java Developers (Silicon Valley Code Camp 13)
Scala for Java Developers (Silicon Valley Code Camp 13)Scala for Java Developers (Silicon Valley Code Camp 13)
Scala for Java Developers (Silicon Valley Code Camp 13)Ramnivas Laddad
 
Scala dreaded underscore
Scala dreaded underscoreScala dreaded underscore
Scala dreaded underscoreRUDDER
 
FCWDS (Foreign Construction Workers Directory System) Introductory Presentation
FCWDS (Foreign Construction Workers Directory System) Introductory PresentationFCWDS (Foreign Construction Workers Directory System) Introductory Presentation
FCWDS (Foreign Construction Workers Directory System) Introductory PresentationKelvin Koh
 
On the development of beliefs vs. capacities: A post-metaphysical view of sec...
On the development of beliefs vs. capacities: A post-metaphysical view of sec...On the development of beliefs vs. capacities: A post-metaphysical view of sec...
On the development of beliefs vs. capacities: A post-metaphysical view of sec...perspegrity5
 
Reading the Campus/Reading the City
Reading the Campus/Reading the CityReading the Campus/Reading the City
Reading the Campus/Reading the CityTina Richardson
 
Sekolah kebangsaan jalan raja syed alwi
Sekolah kebangsaan jalan raja syed alwiSekolah kebangsaan jalan raja syed alwi
Sekolah kebangsaan jalan raja syed alwiNorshida Shida
 
Why scala - executive overview
Why scala - executive overviewWhy scala - executive overview
Why scala - executive overviewRazvan Cojocaru
 
3cork and kerry
3cork and kerry3cork and kerry
3cork and kerryriaenglish
 
The new masters of management
The new masters of managementThe new masters of management
The new masters of managementrsoosaar
 
My SQL Idiosyncrasies That Bite OTN
My SQL Idiosyncrasies That Bite OTNMy SQL Idiosyncrasies That Bite OTN
My SQL Idiosyncrasies That Bite OTNRonald Bradford
 
Product Development Communications Executive Pat Scherschel 2011
Product Development Communications Executive Pat Scherschel 2011Product Development Communications Executive Pat Scherschel 2011
Product Development Communications Executive Pat Scherschel 2011patriciaindy2011
 
Simple c-programs
Simple c-programsSimple c-programs
Simple c-programsrashmi322
 
RK Swamy BBDO Pre-Summers
RK Swamy BBDO Pre-Summers RK Swamy BBDO Pre-Summers
RK Swamy BBDO Pre-Summers shash4rao
 
Sovereignty, Free Will, and Salvation - Limited Atonement
Sovereignty, Free Will, and Salvation - Limited AtonementSovereignty, Free Will, and Salvation - Limited Atonement
Sovereignty, Free Will, and Salvation - Limited AtonementRobin Schumacher
 

Destaque (20)

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

Semelhante a From Java to Scala - advantages and possible risks

pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfHiroshi Ono
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?Tomasz Wrobel
 
The Scala Programming Language
The Scala Programming LanguageThe Scala Programming Language
The Scala Programming Languageleague
 
Functional programming with Scala
Functional programming with ScalaFunctional programming with Scala
Functional programming with ScalaNeelkanth Sachdeva
 
Monadologie
MonadologieMonadologie
Monadologieleague
 
Scala @ TechMeetup Edinburgh
Scala @ TechMeetup EdinburghScala @ TechMeetup Edinburgh
Scala @ TechMeetup EdinburghStuart Roebuck
 
Principles of functional progrmming in scala
Principles of functional progrmming in scalaPrinciples of functional progrmming in scala
Principles of functional progrmming in scalaehsoon
 

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

Coding in Style
Coding in StyleCoding in Style
Coding in Style
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdfpragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
pragmaticrealworldscalajfokus2009-1233251076441384-2.pdf
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?
 
Spark workshop
Spark workshopSpark workshop
Spark workshop
 
Scala ntnu
Scala ntnuScala ntnu
Scala ntnu
 
Scala coated JVM
Scala coated JVMScala coated JVM
Scala coated JVM
 
Scala - brief intro
Scala - brief introScala - brief intro
Scala - brief intro
 
The Scala Programming Language
The Scala Programming LanguageThe Scala Programming Language
The Scala Programming Language
 
Scala for curious
Scala for curiousScala for curious
Scala for curious
 
Functional programming with Scala
Functional programming with ScalaFunctional programming with Scala
Functional programming with Scala
 
Monadologie
MonadologieMonadologie
Monadologie
 
Scala @ TechMeetup Edinburgh
Scala @ TechMeetup EdinburghScala @ TechMeetup Edinburgh
Scala @ TechMeetup Edinburgh
 
Introduction to Scala
Introduction to ScalaIntroduction to Scala
Introduction to Scala
 
Hadoop + Clojure
Hadoop + ClojureHadoop + Clojure
Hadoop + Clojure
 
Principles of functional progrmming in scala
Principles of functional progrmming in scalaPrinciples of functional progrmming in scala
Principles of functional progrmming in scala
 
Hw09 Hadoop + Clojure
Hw09   Hadoop + ClojureHw09   Hadoop + Clojure
Hw09 Hadoop + Clojure
 
An introduction to scala
An introduction to scalaAn introduction to scala
An introduction to scala
 

Mais de SeniorDevOnly

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

Mais de SeniorDevOnly (6)

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

Último

Call Girls Jalahalli Just Call 👗 7737669865 👗 Top Class Call Girl Service Ban...
Call Girls Jalahalli Just Call 👗 7737669865 👗 Top Class Call Girl Service Ban...Call Girls Jalahalli Just Call 👗 7737669865 👗 Top Class Call Girl Service Ban...
Call Girls Jalahalli Just Call 👗 7737669865 👗 Top Class Call Girl Service Ban...amitlee9823
 
Halmar dropshipping via API with DroFx
Halmar  dropshipping  via API with DroFxHalmar  dropshipping  via API with DroFx
Halmar dropshipping via API with DroFxolyaivanovalion
 
Call Girls Indiranagar Just Call 👗 7737669865 👗 Top Class Call Girl Service B...
Call Girls Indiranagar Just Call 👗 7737669865 👗 Top Class Call Girl Service B...Call Girls Indiranagar Just Call 👗 7737669865 👗 Top Class Call Girl Service B...
Call Girls Indiranagar Just Call 👗 7737669865 👗 Top Class Call Girl Service B...amitlee9823
 
➥🔝 7737669865 🔝▻ malwa Call-girls in Women Seeking Men 🔝malwa🔝 Escorts Ser...
➥🔝 7737669865 🔝▻ malwa Call-girls in Women Seeking Men  🔝malwa🔝   Escorts Ser...➥🔝 7737669865 🔝▻ malwa Call-girls in Women Seeking Men  🔝malwa🔝   Escorts Ser...
➥🔝 7737669865 🔝▻ malwa Call-girls in Women Seeking Men 🔝malwa🔝 Escorts Ser...amitlee9823
 
Accredited-Transport-Cooperatives-Jan-2021-Web.pdf
Accredited-Transport-Cooperatives-Jan-2021-Web.pdfAccredited-Transport-Cooperatives-Jan-2021-Web.pdf
Accredited-Transport-Cooperatives-Jan-2021-Web.pdfadriantubila
 
Escorts Service Kumaraswamy Layout ☎ 7737669865☎ Book Your One night Stand (B...
Escorts Service Kumaraswamy Layout ☎ 7737669865☎ Book Your One night Stand (B...Escorts Service Kumaraswamy Layout ☎ 7737669865☎ Book Your One night Stand (B...
Escorts Service Kumaraswamy Layout ☎ 7737669865☎ Book Your One night Stand (B...amitlee9823
 
Call Girls In Doddaballapur Road ☎ 7737669865 🥵 Book Your One night Stand
Call Girls In Doddaballapur Road ☎ 7737669865 🥵 Book Your One night StandCall Girls In Doddaballapur Road ☎ 7737669865 🥵 Book Your One night Stand
Call Girls In Doddaballapur Road ☎ 7737669865 🥵 Book Your One night Standamitlee9823
 
Thane Call Girls 7091864438 Call Girls in Thane Escort service book now -
Thane Call Girls 7091864438 Call Girls in Thane Escort service book now -Thane Call Girls 7091864438 Call Girls in Thane Escort service book now -
Thane Call Girls 7091864438 Call Girls in Thane Escort service book now -Pooja Nehwal
 
Call Girls In Hsr Layout ☎ 7737669865 🥵 Book Your One night Stand
Call Girls In Hsr Layout ☎ 7737669865 🥵 Book Your One night StandCall Girls In Hsr Layout ☎ 7737669865 🥵 Book Your One night Stand
Call Girls In Hsr Layout ☎ 7737669865 🥵 Book Your One night Standamitlee9823
 
CebaBaby dropshipping via API with DroFX.pptx
CebaBaby dropshipping via API with DroFX.pptxCebaBaby dropshipping via API with DroFX.pptx
CebaBaby dropshipping via API with DroFX.pptxolyaivanovalion
 
Jual Obat Aborsi Surabaya ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...
Jual Obat Aborsi Surabaya ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...Jual Obat Aborsi Surabaya ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...
Jual Obat Aborsi Surabaya ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...ZurliaSoop
 
Probability Grade 10 Third Quarter Lessons
Probability Grade 10 Third Quarter LessonsProbability Grade 10 Third Quarter Lessons
Probability Grade 10 Third Quarter LessonsJoseMangaJr1
 
FESE Capital Markets Fact Sheet 2024 Q1.pdf
FESE Capital Markets Fact Sheet 2024 Q1.pdfFESE Capital Markets Fact Sheet 2024 Q1.pdf
FESE Capital Markets Fact Sheet 2024 Q1.pdfMarinCaroMartnezBerg
 
Chintamani Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore ...
Chintamani Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore ...Chintamani Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore ...
Chintamani Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore ...amitlee9823
 
Cheap Rate Call girls Sarita Vihar Delhi 9205541914 shot 1500 night
Cheap Rate Call girls Sarita Vihar Delhi 9205541914 shot 1500 nightCheap Rate Call girls Sarita Vihar Delhi 9205541914 shot 1500 night
Cheap Rate Call girls Sarita Vihar Delhi 9205541914 shot 1500 nightDelhi Call girls
 
5CL-ADBA,5cladba, Chinese supplier, safety is guaranteed
5CL-ADBA,5cladba, Chinese supplier, safety is guaranteed5CL-ADBA,5cladba, Chinese supplier, safety is guaranteed
5CL-ADBA,5cladba, Chinese supplier, safety is guaranteedamy56318795
 
Capstone Project on IBM Data Analytics Program
Capstone Project on IBM Data Analytics ProgramCapstone Project on IBM Data Analytics Program
Capstone Project on IBM Data Analytics ProgramMoniSankarHazra
 
Midocean dropshipping via API with DroFx
Midocean dropshipping via API with DroFxMidocean dropshipping via API with DroFx
Midocean dropshipping via API with DroFxolyaivanovalion
 
Call Girls Indiranagar Just Call 👗 9155563397 👗 Top Class Call Girl Service B...
Call Girls Indiranagar Just Call 👗 9155563397 👗 Top Class Call Girl Service B...Call Girls Indiranagar Just Call 👗 9155563397 👗 Top Class Call Girl Service B...
Call Girls Indiranagar Just Call 👗 9155563397 👗 Top Class Call Girl Service B...only4webmaster01
 

Último (20)

Call Girls Jalahalli Just Call 👗 7737669865 👗 Top Class Call Girl Service Ban...
Call Girls Jalahalli Just Call 👗 7737669865 👗 Top Class Call Girl Service Ban...Call Girls Jalahalli Just Call 👗 7737669865 👗 Top Class Call Girl Service Ban...
Call Girls Jalahalli Just Call 👗 7737669865 👗 Top Class Call Girl Service Ban...
 
Halmar dropshipping via API with DroFx
Halmar  dropshipping  via API with DroFxHalmar  dropshipping  via API with DroFx
Halmar dropshipping via API with DroFx
 
Call Girls Indiranagar Just Call 👗 7737669865 👗 Top Class Call Girl Service B...
Call Girls Indiranagar Just Call 👗 7737669865 👗 Top Class Call Girl Service B...Call Girls Indiranagar Just Call 👗 7737669865 👗 Top Class Call Girl Service B...
Call Girls Indiranagar Just Call 👗 7737669865 👗 Top Class Call Girl Service B...
 
➥🔝 7737669865 🔝▻ malwa Call-girls in Women Seeking Men 🔝malwa🔝 Escorts Ser...
➥🔝 7737669865 🔝▻ malwa Call-girls in Women Seeking Men  🔝malwa🔝   Escorts Ser...➥🔝 7737669865 🔝▻ malwa Call-girls in Women Seeking Men  🔝malwa🔝   Escorts Ser...
➥🔝 7737669865 🔝▻ malwa Call-girls in Women Seeking Men 🔝malwa🔝 Escorts Ser...
 
Accredited-Transport-Cooperatives-Jan-2021-Web.pdf
Accredited-Transport-Cooperatives-Jan-2021-Web.pdfAccredited-Transport-Cooperatives-Jan-2021-Web.pdf
Accredited-Transport-Cooperatives-Jan-2021-Web.pdf
 
Escorts Service Kumaraswamy Layout ☎ 7737669865☎ Book Your One night Stand (B...
Escorts Service Kumaraswamy Layout ☎ 7737669865☎ Book Your One night Stand (B...Escorts Service Kumaraswamy Layout ☎ 7737669865☎ Book Your One night Stand (B...
Escorts Service Kumaraswamy Layout ☎ 7737669865☎ Book Your One night Stand (B...
 
Call Girls In Doddaballapur Road ☎ 7737669865 🥵 Book Your One night Stand
Call Girls In Doddaballapur Road ☎ 7737669865 🥵 Book Your One night StandCall Girls In Doddaballapur Road ☎ 7737669865 🥵 Book Your One night Stand
Call Girls In Doddaballapur Road ☎ 7737669865 🥵 Book Your One night Stand
 
Thane Call Girls 7091864438 Call Girls in Thane Escort service book now -
Thane Call Girls 7091864438 Call Girls in Thane Escort service book now -Thane Call Girls 7091864438 Call Girls in Thane Escort service book now -
Thane Call Girls 7091864438 Call Girls in Thane Escort service book now -
 
Call Girls In Hsr Layout ☎ 7737669865 🥵 Book Your One night Stand
Call Girls In Hsr Layout ☎ 7737669865 🥵 Book Your One night StandCall Girls In Hsr Layout ☎ 7737669865 🥵 Book Your One night Stand
Call Girls In Hsr Layout ☎ 7737669865 🥵 Book Your One night Stand
 
CebaBaby dropshipping via API with DroFX.pptx
CebaBaby dropshipping via API with DroFX.pptxCebaBaby dropshipping via API with DroFX.pptx
CebaBaby dropshipping via API with DroFX.pptx
 
Jual Obat Aborsi Surabaya ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...
Jual Obat Aborsi Surabaya ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...Jual Obat Aborsi Surabaya ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...
Jual Obat Aborsi Surabaya ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...
 
Predicting Loan Approval: A Data Science Project
Predicting Loan Approval: A Data Science ProjectPredicting Loan Approval: A Data Science Project
Predicting Loan Approval: A Data Science Project
 
Probability Grade 10 Third Quarter Lessons
Probability Grade 10 Third Quarter LessonsProbability Grade 10 Third Quarter Lessons
Probability Grade 10 Third Quarter Lessons
 
FESE Capital Markets Fact Sheet 2024 Q1.pdf
FESE Capital Markets Fact Sheet 2024 Q1.pdfFESE Capital Markets Fact Sheet 2024 Q1.pdf
FESE Capital Markets Fact Sheet 2024 Q1.pdf
 
Chintamani Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore ...
Chintamani Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore ...Chintamani Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore ...
Chintamani Call Girls: 🍓 7737669865 🍓 High Profile Model Escorts | Bangalore ...
 
Cheap Rate Call girls Sarita Vihar Delhi 9205541914 shot 1500 night
Cheap Rate Call girls Sarita Vihar Delhi 9205541914 shot 1500 nightCheap Rate Call girls Sarita Vihar Delhi 9205541914 shot 1500 night
Cheap Rate Call girls Sarita Vihar Delhi 9205541914 shot 1500 night
 
5CL-ADBA,5cladba, Chinese supplier, safety is guaranteed
5CL-ADBA,5cladba, Chinese supplier, safety is guaranteed5CL-ADBA,5cladba, Chinese supplier, safety is guaranteed
5CL-ADBA,5cladba, Chinese supplier, safety is guaranteed
 
Capstone Project on IBM Data Analytics Program
Capstone Project on IBM Data Analytics ProgramCapstone Project on IBM Data Analytics Program
Capstone Project on IBM Data Analytics Program
 
Midocean dropshipping via API with DroFx
Midocean dropshipping via API with DroFxMidocean dropshipping via API with DroFx
Midocean dropshipping via API with DroFx
 
Call Girls Indiranagar Just Call 👗 9155563397 👗 Top Class Call Girl Service B...
Call Girls Indiranagar Just Call 👗 9155563397 👗 Top Class Call Girl Service B...Call Girls Indiranagar Just Call 👗 9155563397 👗 Top Class Call Girl Service B...
Call Girls Indiranagar Just Call 👗 9155563397 👗 Top Class Call Girl Service B...
 

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