KEMBAR78
Effective way to code in Scala | ODP
Effective way to code in ScalaEffective way to code in Scala
Satendra Kumar
Software Consultant
Knoldus Software LLP
Topics Covered
➢ Pattern match
➢ Pattern match vs if/else
➢ var vs val
➢ Try/catch
➢ Loop vs recursion
➢ Tuple usage
➢ getOrElse vs fold
➢ Future Composition
➢ Scala ExecutionContext
➢ Play ExecutionContext
➢ Akka ExecutionContext
Pattern Match
trait Arithmetic
case class Add(a: Int, b: Int) extends Arithmetic
case class Subtract(a: Int, b: Int) extends Arithmetic
case class Multiply(a: Int, b: Int) extends Arithmetic
def calculate(expr: Arithmetic): Int =
expr match {
case Add(a, b) => a + b
case Subtract(a, b) => a + b
case Multiply(a, b) => a + b
}
Pattern Match
trait Arithmetic
case class Add(a: Int, b: Int) extends Arithmetic
case class Subtract(a: Int, b: Int) extends Arithmetic
case class Multiply(a: Int, b: Int) extends Arithmetic
def calculate(expr: Arithmetic): Int =
expr match {
case Add(a, b) => a + b
case Subtract(a, b) => a + b
case Multiply(a, b) => a + b
}
Calling from another class:
calculate(Add(2,3))
// output => 5
Pattern Match
trait Arithmetic
case class Add(a: Int, b: Int) extends Arithmetic
case class Subtract(a: Int, b: Int) extends Arithmetic
case class Multiply(a: Int, b: Int) extends Arithmetic
def calculate(expr: Arithmetic): Int =
expr match {
case Add(a, b) => a + b
case Subtract(a, b) => a + b
case Multiply(a, b) => a + b
}
//Calling from another class:
case class IncrementByOne(number: Int) extends Arithmetic
calculate(IncrementByOne(2))
// output => ?
Pattern Match
trait Arithmetic
case class Add(a: Int, b: Int) extends Arithmetic
case class Subtract(a: Int, b: Int) extends Arithmetic
case class Multiply(a: Int, b: Int) extends Arithmetic
def calculate(expr: Arithmetic): Int =
expr match {
case Add(a, b) => a + b
case Subtract(a, b) => a + b
case Multiply(a, b) => a + b
}
//Calling from another class:
case class IncrementByOne(number: Int) extends Arithmetic
calculate(IncrementByOne(2))
// output => Match error
Pattern Match
sealed trait Arithmetic
case class Add(a: Int, b: Int) extends Arithmetic
case class Subtract(a: Int, b: Int) extends Arithmetic
case class Multiply(a: Int, b: Int) extends Arithmetic
def calculate(expr: Arithmetic): Int =
expr match {
case Add(a, b) => a + b
case Subtract(a, b) => a + b
case Multiply(a, b) => a + b
}
Alway use sealed keyword to avoid match error.
Pattern match
sealed trait Arithmetic
case class Add(a: Int, b: Int) extends Arithmetic
case class Subtract(a: Int, b: Int) extends Arithmetic
case class Multiply(a: Int, b: Int) extends Arithmetic
case class IncrementByOne(number: Int) extends Arithmetic
val exprs = List(Add(2, 3), Subtract(5, 3), Multiply(2, 3))
exprs map { expr =>
expr match {
case Add(a, b) => a + b
case Subtract(a, b) => a + b
case Multiply(a, b) => a + b
case IncrementByOne(a) => a + 1
}
}
Pattern match
sealed trait Arithmetic
case class Add(a: Int, b: Int) extends Arithmetic
case class Subtract(a: Int, b: Int) extends Arithmetic
case class Multiply(a: Int, b: Int) extends Arithmetic
case class IncrementByOne(number: Int) extends Arithmetic
val exprs = List(Add(2, 3), Subtract(5, 3), Multiply(2, 3))
exprs map { expr =>
expr match {
case Add(a, b) => a + b
case Subtract(a, b) => a + b
case Multiply(a, b) => a + b
case IncrementByOne(a) => a + 1
}
}
Improvement ?
Pattern Match
val exprs = List(Add(2, 3), Subtract(5, 3), Multiply(2, 3))
exprs map {
case Add(a, b) => a + b
case Subtract(a, b) => a + b
case Multiply(a, b) => a + b
case IncrementByOne(a) => a + 1
}
Pattern Match
def calculate(expr: Arithmetic): Int =
expr match {
case Add(a, b) => a + b
case Subtract(a, b) => a + b
case Multiply(a, b) => a + b
case IncrementByOne(a) => a + 1
case IncrementByTwo(a) => a + 2
case IncrementByThree(a) => a + 3
case IncrementByFour(a) => a + 4
case IncrementByFive(a) => a + 5
case IncrementBySix(a) => a + 6
case IncrementBySeven(a) => a + 7
case IncrementByEight(a) => a + 8
}
Cyclomatic complexity > 10
Pattern Match
def calculate(expr: Arithmetic): Int = binaryOperation.orElse(increment)(expr)
val binaryOperation: PartialFunction[Arithmetic, Int] = {
case Add(a, b) => a + b
case Subtract(a, b) => a + b
case Multiply(a, b) => a + b
}
val increment: PartialFunction[Arithmetic, Int] = {
case IncrementByOne(a) => a + 1
case IncrementByTwo(a) => a + 2
case IncrementByThree(a) => a + 3
case IncrementByFour(a) => a + 4
case IncrementByFive(a) => a + 5
case IncrementBySix(a) => a + 6
case IncrementBySeven(a) => a + 7
case IncrementByEight(a) => a + 8
}
Pattern Match
def method1(flag: Boolean):String =
flag match {
case true => "TRUE"
case false => "FALSE"
}
def method2(flag: Boolean): String =
if (flag)
"TRUE"
else
"FALSE"
public java.lang.String method1(boolean);
Code:
0: iload_1
1: istore_2
2: iconst_1
3: iload_2
4: if_icmpne 13
7: ldc #9
9: astore_3
10: goto 21
13: iconst_0
14: iload_2
15: if_icmpne 23
18: ldc #11
20: astore_3
21: aload_3
22: areturn
23: new #13
26: dup
27: iload_2
28: invokestatic #19
31: invokespecial #23
34: athrow
public java.lang.String method2(boolean);
Code:
0: iload_1
1: ifeq 9
4: ldc #9
6: goto 11
9: ldc #11
11: areturn
public java.lang.String method1(boolean);
Code:
0: iload_1
1: istore_2
2: iconst_1
3: iload_2
4: if_icmpne 13
7: ldc #9
9: astore_3
10: goto 21
13: iconst_0
14: iload_2
15: if_icmpne 23
18: ldc #11
20: astore_3
21: aload_3
22: areturn
23: new #13
26: dup
27: iload_2
28: invokestatic #19
31: invokespecial #23
34: athrow
public java.lang.String method2(boolean);
Code:
0: iload_1
1: ifeq 9
4: ldc #9
6: goto 11
9: ldc #11
11: areturn
Avoid pattern match on boolean value
If/else
def method(flag: Boolean): String =
if (flag == true)
"TRUE"
else
"FALSE"
What is the problem ?
If/else
//Incorrect
def method(flag: Boolean): String =
if (flag == true)
"TRUE"
else
"FALSE"
//correct
def method(flag: Boolean): String =
if (flag)
"TRUE"
else
"FALSE"
val vs var
Variable type Collection type Example
val vs var
Variable type Collection type Example
Immutable Immutable Best
val map = Map("name" -> "sky")
val vs var
Variable type Collection type Example
Immutable Immutable Best
Mutable Immutable Good
val map = Map("name" -> "sky")
var map = Map("name" -> "sky")
val vs var
Variable type Collection type Example
Immutable Immutable Best
Mutable Immutable Good
Immutable Mutable Ok
val map = Map("name" -> "sky")
val map = mutable.Map("name" -> "sky")
var map = Map("name" -> "sky")
val vs var
Variable type Collection type Example
Immutable Immutable Best
Mutable Immutable Good
Immutable Mutable Ok
Mutable Mutable worst
val map = Map("name" -> "sky")
val map = mutable.Map("name" -> "sky")
var map = Map("name" -> "sky")
var map = mutable.Map("name" -> "sky")
val vs var
Variable type Collection type Example
Immutable Immutable Best
Mutable Immutable Good
Immutable Mutable Ok
Mutable Mutable worst
val map = Map("name" -> "sky")
val map = mutable.Map("name" -> "sky")
var map = Map("name" -> "sky")
var map = mutable.Map("name" -> "sky")
Never use combination of both mutable variable and mutable collection
Try/catch
try {
doSomething()
} catch {
case th: Throwable =>
someHandler()
}
Try/catch
try {
doSomething()
} catch {
case th: Throwable =>
someHandler()
}
MUST NOT catch Throwable when catching Exceptions
Try/catch
import scala.util.control.NonFatal
try {
doSomething()
} catch {
case NonFatal(ex) =>
someHandler()
}
Try/catch
object NonFatal {
/**
* Returns true if the provided `Throwable` is to be considered non-fatal, or
false if it is to be considered fatal
*/
def apply(t: Throwable): Boolean = t match {
// VirtualMachineError includes OutOfMemoryError and other fatal errors
case _: VirtualMachineError | _: ThreadDeath |
_: InterruptedException | _: LinkageError | _: ControlThrowable => false
case _ => true
}
/**
* Returns Some(t) if NonFatal(t) == true, otherwise None
*/
def unapply(t: Throwable): Option[Throwable] = if (apply(t)) Some(t) else None
}
Loop vs recursion
def factorialUsingLoop(n: Int): Int = {
var accumulator = 1
var i = 1
while (i <= n) {
accumulator = i * accumulator
i += 1
}
accumulator
}
def factorialUsingRecursion(n: Int): Int = {
@tailrec
def fac(n: Int, acc: Int): Int = n match {
case _ if n == 1 => acc
case _ => fac(n - 1, n * acc)
}
fac(n, 1)
}
Tuple usage
def wordCount(text: String): List[(String, Int)] =
text.split(" +").groupBy(word => word)
.map { word => (word._1, word._2.length) }
.toList
.sortBy(_._2)
.reverse
Tuple usage
def wordCount(text: String): List[(String, Int)] =
text.split(" +").groupBy(word => word)
.map { word => (word._1, word._2.length) }
.toList
.sortBy(_._2)
.reverse
def wordCount(text: String): List[(String, Int)] =
text.split(" +").groupBy(identity)
.map { case (word, frequency) => (word, frequency.length) }
.toList
.sortBy { case (_, count) => count }
.reverse
// More Readable
Tuple usage
def wordCount(text: String): List[(String, Int)] =
text.split(" +").groupBy(word => word)
.map { word => (word._1, word._2.length) }
.toList
.sortBy(_._2)
.reverse
def wordCount(text: String): List[(String, Int)] =
text.split(" +").groupBy(identity)
.map { case (word, frequency) => (word, frequency.length) }
.toList
.sortBy { case (_, count) => count }
.reverse
// More Readable
Avoid _1 and _2 method for accessing tuple value
getOrElse vs fold
val optionalInt:Option[Int]=Some(1)
val intValue= optionalInt.getOrElse("0")
getOrElse vs fold
val optionalInt:Option[Int]=Some(1)
val intValue= optionalInt.getOrElse("0")
String value
getOrElse vs fold
val optionalInt:Option[Int]=Some(1)
val intValue= optionalInt.getOrElse("0")
String value
Type of intValue become Any
val intValue= optionalInt.fold("0")(v =>v)
getOrElse vs fold
val optionalInt:Option[Int]=Some(1)
val intValue= optionalInt.getOrElse("0")
String value
Type of intValue become Any
val intValue= optionalInt.fold("0")(v =>v)
Compilation Error
getOrElse vs fold
val optionalInt:Option[Int]=Some(1)
val intValue= optionalInt.getOrElse("0")
String value
Type of intValue become Any
val intValue= optionalInt.fold(0)(v =>v)
getOrElse is not typesafe so use fold instead of getOrElse
head and get
val list =List[Int]().head
head and get
val list =List[Int]().head
Exception: head of empty list
val list =List[Int]().headOption
val optionalInt: Option[Int] = None
val intValue = optionalInt.get
head and get
val list =List[Int]().head
Exception: head of empty list
val list =List[Int]().headOption
val optionalInt: Option[Int] = None
val intValue = optionalInt.get
scala.None$.get
val intValue = optionalInt.fold(0)(v=>v)
head and get
val list =List[Int]().head
Exception: head of empty list
val list =List[Int]().headOption
val optionalInt: Option[Int] = None
val intValue = optionalInt.get
scala.None$.get
val intValue = optionalInt.fold(0)(v=>v)
Alway Avoid head and get method
Loop vs recursion
def factorialUsingLoop(n: Int): Int = {
var accumulator = 1
var i = 1
while (i <= n) {
accumulator = i * accumulator
i += 1
}
accumulator
}
def factorialUsingRecursion(n: Int): Int = {
@tailrec
def fac(n: Int, acc: Int): Int = n match {
case _ if n == 1 => acc
case _ => fac(n - 1, n * acc)
}
fac(n, 1)
}
Avoid looping if possible and use tail recursion
Future Composition
def composingFuture(): Future[Int] = {
val firstFuture: Future[Int] = method1()
val secondFuture: Future[Int] = firstFuture.flatMap { v => method2(v) }
val thirdFuture: Future[Int] = secondFuture.flatMap { v => method3(v) }
val fourthFuture: Future[Int] = method4() // no composition
val result: Future[Int] = thirdFuture.flatMap { v => method5(v) }
result
}
def method1(): Future[Int] = ???
def method2(value: Int): Future[Int] = ???
def method3(value: Int): Future[Int] = ???
def method4(): Future[Int] = ???
def method5(value: Int): Future[Int] = ???
Future Composition
def composingFuture(): Future[Int] = {
val firstFuture: Future[Int] = method1()
val secondFuture: Future[Int] = firstFuture.flatMap { v => method2(v) }
val thirdFuture: Future[Int] = secondFuture.flatMap { v => method3(v) }
val fourthFuture: Future[Int] = method4() // no composition
val result: Future[Int] = thirdFuture.flatMap { v => method5(v) }
result
}
def method1(): Future[Int] = ???
def method2(value: Int): Future[Int] = ???
def method3(value: Int): Future[Int] = ???
def method4(): Future[Int] = ???
def method5(value: Int): Future[Int] = ???
what is the problem ?
Future Composition
def composingFuture(): Future[Int] = {
val firstFuture: Future[Int] = method1()
val secondFuture: Future[Int] = firstFuture.flatMap { v => method2(v) }
val thirdFuture: Future[Int] = secondFuture.flatMap { v => method3(v) }
val fourthFuture: Future[Int] = method4()
val result: Future[Int] = thirdFuture.flatMap { v => method5(v) }
fourthFuture.flatMap{ _=> result}// composing fourth future value
}
def method1(): Future[Int] = ???
def method2(value: Int): Future[Int] = ???
def method3(value: Int): Future[Int] = ???
def method4(): Future[Int] = ???
def method5(value: Int): Future[Int] = ???
Fixed Value for Future
import scala.concurrent.ExecutionContext.Implicits.global
def calculate(value: Option[Int]): Future[Int] =
value match {
case Some(v) => method(v)
case None => Future(0)
}
def method(value: Int): Future[Int] = ???
Fixed Value for Future
def calculate(value: Option[Int]): Future[Int] =
value match {
case Some(v) => method(v)
case None => Future.successful(0)
}
def method(value: Int): Future[Int] = ???
Alway use Future.successful for fixed value and Future.failed for exception
Scala ExecutionContext
➢ ForkJoinPool
➢ scala.concurrent.context.minThreads = “1”
➢ scala.concurrent.context.numThreads =”x1”
x1 means => Runtime.getRuntime.availableProcessors * 1
➢ scala.concurrent.context.maxThreads ="x1”
➢ Avalable in scala.concurrent.ExecutionContext.Implicits.global
Scala ExecutionContext
➢ ForkJoinPool
➢ scala.concurrent.context.minThreads = “1”
➢ scala.concurrent.context.numThreads =”x1”
x1 means => Runtime.getRuntime.availableProcessors * 1
➢ scala.concurrent.context.maxThreads ="x1”
➢ Avalable in scala.concurrent.ExecutionContext.Implicits.global
Don't use Scala ExecutionContext for IO operations
For heavy IO
import java.util.concurrent.Executors
object ExecutionContext {
///read from config or environment
val CONCURRENCY_FACTOR = 3
object IO {
/**
* Responsible to handle all DB calls
*/
implicit lazy val dbOperations: concurrent.ExecutionContext =
concurrent.ExecutionContext
.fromExecutor(
Executors.newFixedThreadPool(
Runtime.getRuntime.availableProcessors() * CONCURRENCY_FACTOR
)
)
}
}
For light weight IO
import java.util.concurrent.Executors
object ExecutionContext {
object IO {
/**
* Responsible to handle all light weight IO
*/
implicit lazy val simpleOperations: concurrent.ExecutionContext =
concurrent.ExecutionContext.fromExecutor(Executors.newCachedThreadPool())
}
}
Play ExecutionContext
● Available in play.api.libs.concurrent.Execution.Implicits.defaultContext
● default configuration for Play’s thread pool:
akka {
actor {
default-dispatcher {
fork-join-executor {
parallelism-factor = 1.0
parallelism-max = 24
# Setting this to LIFO changes the fork-join-executor
# to use a stack discipline for task scheduling. This usually
# improves throughput at the cost of possibly increasing
# latency and risking task starvation (which should be rare).
task-peeking-mode = LIFO
}
}
}
}
Akka ExecutionContext
The default Akka configuration:
akka {
actor {
default-dispatcher {
fork-join-executor {
parallelism-min = 8
parallelism-factor = 3.0
parallelism-max = 64
task-peeking-mode = "FIFO"
}
}
}
}
For light weight IO
For IO use thread-pool-executor.
For Example:
my-thread-pool-dispatcher {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
core-pool-size-min = 20
core-pool-size-factor = 20
core-pool-size-max = 100
}
throughput = 1
}
For Heavy IO
For IO use thread-pool-executor.
For Example:
my-thread-pool-dispatcher {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
core-pool-size-min = 8
core-pool-size-factor = 3
core-pool-size-max = 16
}
throughput = 1
}
Thump Rule
● In Scala, use Scala ExecutionContext
● In Play, use Play ExecutionContext
● In Akka, use Akka ExecutionContext
● In Akka, One ActorSystem per Application
References
● http://doc.akka.io
● https://www.playframework.com/documentation/2.5
● http://twitter.github.io/effectivescala
● https://github.com/alexandru/scala-best-practices
Thanks

Effective way to code in Scala

  • 1.
    Effective way tocode in ScalaEffective way to code in Scala Satendra Kumar Software Consultant Knoldus Software LLP
  • 2.
    Topics Covered ➢ Patternmatch ➢ Pattern match vs if/else ➢ var vs val ➢ Try/catch ➢ Loop vs recursion ➢ Tuple usage ➢ getOrElse vs fold ➢ Future Composition ➢ Scala ExecutionContext ➢ Play ExecutionContext ➢ Akka ExecutionContext
  • 3.
    Pattern Match trait Arithmetic caseclass Add(a: Int, b: Int) extends Arithmetic case class Subtract(a: Int, b: Int) extends Arithmetic case class Multiply(a: Int, b: Int) extends Arithmetic def calculate(expr: Arithmetic): Int = expr match { case Add(a, b) => a + b case Subtract(a, b) => a + b case Multiply(a, b) => a + b }
  • 4.
    Pattern Match trait Arithmetic caseclass Add(a: Int, b: Int) extends Arithmetic case class Subtract(a: Int, b: Int) extends Arithmetic case class Multiply(a: Int, b: Int) extends Arithmetic def calculate(expr: Arithmetic): Int = expr match { case Add(a, b) => a + b case Subtract(a, b) => a + b case Multiply(a, b) => a + b } Calling from another class: calculate(Add(2,3)) // output => 5
  • 5.
    Pattern Match trait Arithmetic caseclass Add(a: Int, b: Int) extends Arithmetic case class Subtract(a: Int, b: Int) extends Arithmetic case class Multiply(a: Int, b: Int) extends Arithmetic def calculate(expr: Arithmetic): Int = expr match { case Add(a, b) => a + b case Subtract(a, b) => a + b case Multiply(a, b) => a + b } //Calling from another class: case class IncrementByOne(number: Int) extends Arithmetic calculate(IncrementByOne(2)) // output => ?
  • 6.
    Pattern Match trait Arithmetic caseclass Add(a: Int, b: Int) extends Arithmetic case class Subtract(a: Int, b: Int) extends Arithmetic case class Multiply(a: Int, b: Int) extends Arithmetic def calculate(expr: Arithmetic): Int = expr match { case Add(a, b) => a + b case Subtract(a, b) => a + b case Multiply(a, b) => a + b } //Calling from another class: case class IncrementByOne(number: Int) extends Arithmetic calculate(IncrementByOne(2)) // output => Match error
  • 7.
    Pattern Match sealed traitArithmetic case class Add(a: Int, b: Int) extends Arithmetic case class Subtract(a: Int, b: Int) extends Arithmetic case class Multiply(a: Int, b: Int) extends Arithmetic def calculate(expr: Arithmetic): Int = expr match { case Add(a, b) => a + b case Subtract(a, b) => a + b case Multiply(a, b) => a + b } Alway use sealed keyword to avoid match error.
  • 8.
    Pattern match sealed traitArithmetic case class Add(a: Int, b: Int) extends Arithmetic case class Subtract(a: Int, b: Int) extends Arithmetic case class Multiply(a: Int, b: Int) extends Arithmetic case class IncrementByOne(number: Int) extends Arithmetic val exprs = List(Add(2, 3), Subtract(5, 3), Multiply(2, 3)) exprs map { expr => expr match { case Add(a, b) => a + b case Subtract(a, b) => a + b case Multiply(a, b) => a + b case IncrementByOne(a) => a + 1 } }
  • 9.
    Pattern match sealed traitArithmetic case class Add(a: Int, b: Int) extends Arithmetic case class Subtract(a: Int, b: Int) extends Arithmetic case class Multiply(a: Int, b: Int) extends Arithmetic case class IncrementByOne(number: Int) extends Arithmetic val exprs = List(Add(2, 3), Subtract(5, 3), Multiply(2, 3)) exprs map { expr => expr match { case Add(a, b) => a + b case Subtract(a, b) => a + b case Multiply(a, b) => a + b case IncrementByOne(a) => a + 1 } } Improvement ?
  • 10.
    Pattern Match val exprs= List(Add(2, 3), Subtract(5, 3), Multiply(2, 3)) exprs map { case Add(a, b) => a + b case Subtract(a, b) => a + b case Multiply(a, b) => a + b case IncrementByOne(a) => a + 1 }
  • 11.
    Pattern Match def calculate(expr:Arithmetic): Int = expr match { case Add(a, b) => a + b case Subtract(a, b) => a + b case Multiply(a, b) => a + b case IncrementByOne(a) => a + 1 case IncrementByTwo(a) => a + 2 case IncrementByThree(a) => a + 3 case IncrementByFour(a) => a + 4 case IncrementByFive(a) => a + 5 case IncrementBySix(a) => a + 6 case IncrementBySeven(a) => a + 7 case IncrementByEight(a) => a + 8 } Cyclomatic complexity > 10
  • 12.
    Pattern Match def calculate(expr:Arithmetic): Int = binaryOperation.orElse(increment)(expr) val binaryOperation: PartialFunction[Arithmetic, Int] = { case Add(a, b) => a + b case Subtract(a, b) => a + b case Multiply(a, b) => a + b } val increment: PartialFunction[Arithmetic, Int] = { case IncrementByOne(a) => a + 1 case IncrementByTwo(a) => a + 2 case IncrementByThree(a) => a + 3 case IncrementByFour(a) => a + 4 case IncrementByFive(a) => a + 5 case IncrementBySix(a) => a + 6 case IncrementBySeven(a) => a + 7 case IncrementByEight(a) => a + 8 }
  • 13.
    Pattern Match def method1(flag:Boolean):String = flag match { case true => "TRUE" case false => "FALSE" } def method2(flag: Boolean): String = if (flag) "TRUE" else "FALSE"
  • 14.
    public java.lang.String method1(boolean); Code: 0:iload_1 1: istore_2 2: iconst_1 3: iload_2 4: if_icmpne 13 7: ldc #9 9: astore_3 10: goto 21 13: iconst_0 14: iload_2 15: if_icmpne 23 18: ldc #11 20: astore_3 21: aload_3 22: areturn 23: new #13 26: dup 27: iload_2 28: invokestatic #19 31: invokespecial #23 34: athrow public java.lang.String method2(boolean); Code: 0: iload_1 1: ifeq 9 4: ldc #9 6: goto 11 9: ldc #11 11: areturn
  • 15.
    public java.lang.String method1(boolean); Code: 0:iload_1 1: istore_2 2: iconst_1 3: iload_2 4: if_icmpne 13 7: ldc #9 9: astore_3 10: goto 21 13: iconst_0 14: iload_2 15: if_icmpne 23 18: ldc #11 20: astore_3 21: aload_3 22: areturn 23: new #13 26: dup 27: iload_2 28: invokestatic #19 31: invokespecial #23 34: athrow public java.lang.String method2(boolean); Code: 0: iload_1 1: ifeq 9 4: ldc #9 6: goto 11 9: ldc #11 11: areturn Avoid pattern match on boolean value
  • 16.
    If/else def method(flag: Boolean):String = if (flag == true) "TRUE" else "FALSE" What is the problem ?
  • 17.
    If/else //Incorrect def method(flag: Boolean):String = if (flag == true) "TRUE" else "FALSE" //correct def method(flag: Boolean): String = if (flag) "TRUE" else "FALSE"
  • 18.
    val vs var Variabletype Collection type Example
  • 19.
    val vs var Variabletype Collection type Example Immutable Immutable Best val map = Map("name" -> "sky")
  • 20.
    val vs var Variabletype Collection type Example Immutable Immutable Best Mutable Immutable Good val map = Map("name" -> "sky") var map = Map("name" -> "sky")
  • 21.
    val vs var Variabletype Collection type Example Immutable Immutable Best Mutable Immutable Good Immutable Mutable Ok val map = Map("name" -> "sky") val map = mutable.Map("name" -> "sky") var map = Map("name" -> "sky")
  • 22.
    val vs var Variabletype Collection type Example Immutable Immutable Best Mutable Immutable Good Immutable Mutable Ok Mutable Mutable worst val map = Map("name" -> "sky") val map = mutable.Map("name" -> "sky") var map = Map("name" -> "sky") var map = mutable.Map("name" -> "sky")
  • 23.
    val vs var Variabletype Collection type Example Immutable Immutable Best Mutable Immutable Good Immutable Mutable Ok Mutable Mutable worst val map = Map("name" -> "sky") val map = mutable.Map("name" -> "sky") var map = Map("name" -> "sky") var map = mutable.Map("name" -> "sky") Never use combination of both mutable variable and mutable collection
  • 24.
    Try/catch try { doSomething() } catch{ case th: Throwable => someHandler() }
  • 25.
    Try/catch try { doSomething() } catch{ case th: Throwable => someHandler() } MUST NOT catch Throwable when catching Exceptions
  • 26.
    Try/catch import scala.util.control.NonFatal try { doSomething() }catch { case NonFatal(ex) => someHandler() }
  • 27.
    Try/catch object NonFatal { /** *Returns true if the provided `Throwable` is to be considered non-fatal, or false if it is to be considered fatal */ def apply(t: Throwable): Boolean = t match { // VirtualMachineError includes OutOfMemoryError and other fatal errors case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable => false case _ => true } /** * Returns Some(t) if NonFatal(t) == true, otherwise None */ def unapply(t: Throwable): Option[Throwable] = if (apply(t)) Some(t) else None }
  • 28.
    Loop vs recursion deffactorialUsingLoop(n: Int): Int = { var accumulator = 1 var i = 1 while (i <= n) { accumulator = i * accumulator i += 1 } accumulator } def factorialUsingRecursion(n: Int): Int = { @tailrec def fac(n: Int, acc: Int): Int = n match { case _ if n == 1 => acc case _ => fac(n - 1, n * acc) } fac(n, 1) }
  • 29.
    Tuple usage def wordCount(text:String): List[(String, Int)] = text.split(" +").groupBy(word => word) .map { word => (word._1, word._2.length) } .toList .sortBy(_._2) .reverse
  • 30.
    Tuple usage def wordCount(text:String): List[(String, Int)] = text.split(" +").groupBy(word => word) .map { word => (word._1, word._2.length) } .toList .sortBy(_._2) .reverse def wordCount(text: String): List[(String, Int)] = text.split(" +").groupBy(identity) .map { case (word, frequency) => (word, frequency.length) } .toList .sortBy { case (_, count) => count } .reverse // More Readable
  • 31.
    Tuple usage def wordCount(text:String): List[(String, Int)] = text.split(" +").groupBy(word => word) .map { word => (word._1, word._2.length) } .toList .sortBy(_._2) .reverse def wordCount(text: String): List[(String, Int)] = text.split(" +").groupBy(identity) .map { case (word, frequency) => (word, frequency.length) } .toList .sortBy { case (_, count) => count } .reverse // More Readable Avoid _1 and _2 method for accessing tuple value
  • 32.
    getOrElse vs fold valoptionalInt:Option[Int]=Some(1) val intValue= optionalInt.getOrElse("0")
  • 33.
    getOrElse vs fold valoptionalInt:Option[Int]=Some(1) val intValue= optionalInt.getOrElse("0") String value
  • 34.
    getOrElse vs fold valoptionalInt:Option[Int]=Some(1) val intValue= optionalInt.getOrElse("0") String value Type of intValue become Any val intValue= optionalInt.fold("0")(v =>v)
  • 35.
    getOrElse vs fold valoptionalInt:Option[Int]=Some(1) val intValue= optionalInt.getOrElse("0") String value Type of intValue become Any val intValue= optionalInt.fold("0")(v =>v) Compilation Error
  • 36.
    getOrElse vs fold valoptionalInt:Option[Int]=Some(1) val intValue= optionalInt.getOrElse("0") String value Type of intValue become Any val intValue= optionalInt.fold(0)(v =>v) getOrElse is not typesafe so use fold instead of getOrElse
  • 37.
    head and get vallist =List[Int]().head
  • 38.
    head and get vallist =List[Int]().head Exception: head of empty list val list =List[Int]().headOption val optionalInt: Option[Int] = None val intValue = optionalInt.get
  • 39.
    head and get vallist =List[Int]().head Exception: head of empty list val list =List[Int]().headOption val optionalInt: Option[Int] = None val intValue = optionalInt.get scala.None$.get val intValue = optionalInt.fold(0)(v=>v)
  • 40.
    head and get vallist =List[Int]().head Exception: head of empty list val list =List[Int]().headOption val optionalInt: Option[Int] = None val intValue = optionalInt.get scala.None$.get val intValue = optionalInt.fold(0)(v=>v) Alway Avoid head and get method
  • 41.
    Loop vs recursion deffactorialUsingLoop(n: Int): Int = { var accumulator = 1 var i = 1 while (i <= n) { accumulator = i * accumulator i += 1 } accumulator } def factorialUsingRecursion(n: Int): Int = { @tailrec def fac(n: Int, acc: Int): Int = n match { case _ if n == 1 => acc case _ => fac(n - 1, n * acc) } fac(n, 1) } Avoid looping if possible and use tail recursion
  • 42.
    Future Composition def composingFuture():Future[Int] = { val firstFuture: Future[Int] = method1() val secondFuture: Future[Int] = firstFuture.flatMap { v => method2(v) } val thirdFuture: Future[Int] = secondFuture.flatMap { v => method3(v) } val fourthFuture: Future[Int] = method4() // no composition val result: Future[Int] = thirdFuture.flatMap { v => method5(v) } result } def method1(): Future[Int] = ??? def method2(value: Int): Future[Int] = ??? def method3(value: Int): Future[Int] = ??? def method4(): Future[Int] = ??? def method5(value: Int): Future[Int] = ???
  • 43.
    Future Composition def composingFuture():Future[Int] = { val firstFuture: Future[Int] = method1() val secondFuture: Future[Int] = firstFuture.flatMap { v => method2(v) } val thirdFuture: Future[Int] = secondFuture.flatMap { v => method3(v) } val fourthFuture: Future[Int] = method4() // no composition val result: Future[Int] = thirdFuture.flatMap { v => method5(v) } result } def method1(): Future[Int] = ??? def method2(value: Int): Future[Int] = ??? def method3(value: Int): Future[Int] = ??? def method4(): Future[Int] = ??? def method5(value: Int): Future[Int] = ??? what is the problem ?
  • 44.
    Future Composition def composingFuture():Future[Int] = { val firstFuture: Future[Int] = method1() val secondFuture: Future[Int] = firstFuture.flatMap { v => method2(v) } val thirdFuture: Future[Int] = secondFuture.flatMap { v => method3(v) } val fourthFuture: Future[Int] = method4() val result: Future[Int] = thirdFuture.flatMap { v => method5(v) } fourthFuture.flatMap{ _=> result}// composing fourth future value } def method1(): Future[Int] = ??? def method2(value: Int): Future[Int] = ??? def method3(value: Int): Future[Int] = ??? def method4(): Future[Int] = ??? def method5(value: Int): Future[Int] = ???
  • 45.
    Fixed Value forFuture import scala.concurrent.ExecutionContext.Implicits.global def calculate(value: Option[Int]): Future[Int] = value match { case Some(v) => method(v) case None => Future(0) } def method(value: Int): Future[Int] = ???
  • 46.
    Fixed Value forFuture def calculate(value: Option[Int]): Future[Int] = value match { case Some(v) => method(v) case None => Future.successful(0) } def method(value: Int): Future[Int] = ??? Alway use Future.successful for fixed value and Future.failed for exception
  • 47.
    Scala ExecutionContext ➢ ForkJoinPool ➢scala.concurrent.context.minThreads = “1” ➢ scala.concurrent.context.numThreads =”x1” x1 means => Runtime.getRuntime.availableProcessors * 1 ➢ scala.concurrent.context.maxThreads ="x1” ➢ Avalable in scala.concurrent.ExecutionContext.Implicits.global
  • 48.
    Scala ExecutionContext ➢ ForkJoinPool ➢scala.concurrent.context.minThreads = “1” ➢ scala.concurrent.context.numThreads =”x1” x1 means => Runtime.getRuntime.availableProcessors * 1 ➢ scala.concurrent.context.maxThreads ="x1” ➢ Avalable in scala.concurrent.ExecutionContext.Implicits.global Don't use Scala ExecutionContext for IO operations
  • 49.
    For heavy IO importjava.util.concurrent.Executors object ExecutionContext { ///read from config or environment val CONCURRENCY_FACTOR = 3 object IO { /** * Responsible to handle all DB calls */ implicit lazy val dbOperations: concurrent.ExecutionContext = concurrent.ExecutionContext .fromExecutor( Executors.newFixedThreadPool( Runtime.getRuntime.availableProcessors() * CONCURRENCY_FACTOR ) ) } }
  • 50.
    For light weightIO import java.util.concurrent.Executors object ExecutionContext { object IO { /** * Responsible to handle all light weight IO */ implicit lazy val simpleOperations: concurrent.ExecutionContext = concurrent.ExecutionContext.fromExecutor(Executors.newCachedThreadPool()) } }
  • 51.
    Play ExecutionContext ● Availablein play.api.libs.concurrent.Execution.Implicits.defaultContext ● default configuration for Play’s thread pool: akka { actor { default-dispatcher { fork-join-executor { parallelism-factor = 1.0 parallelism-max = 24 # Setting this to LIFO changes the fork-join-executor # to use a stack discipline for task scheduling. This usually # improves throughput at the cost of possibly increasing # latency and risking task starvation (which should be rare). task-peeking-mode = LIFO } } } }
  • 52.
    Akka ExecutionContext The defaultAkka configuration: akka { actor { default-dispatcher { fork-join-executor { parallelism-min = 8 parallelism-factor = 3.0 parallelism-max = 64 task-peeking-mode = "FIFO" } } } }
  • 53.
    For light weightIO For IO use thread-pool-executor. For Example: my-thread-pool-dispatcher { type = Dispatcher executor = "thread-pool-executor" thread-pool-executor { core-pool-size-min = 20 core-pool-size-factor = 20 core-pool-size-max = 100 } throughput = 1 }
  • 54.
    For Heavy IO ForIO use thread-pool-executor. For Example: my-thread-pool-dispatcher { type = Dispatcher executor = "thread-pool-executor" thread-pool-executor { core-pool-size-min = 8 core-pool-size-factor = 3 core-pool-size-max = 16 } throughput = 1 }
  • 55.
    Thump Rule ● InScala, use Scala ExecutionContext ● In Play, use Play ExecutionContext ● In Akka, use Akka ExecutionContext ● In Akka, One ActorSystem per Application
  • 56.
    References ● http://doc.akka.io ● https://www.playframework.com/documentation/2.5 ●http://twitter.github.io/effectivescala ● https://github.com/alexandru/scala-best-practices
  • 57.