Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Effective way to code in Scala
1. Effective way to code in ScalaEffective way to code in Scala
Satendra Kumar
Software Consultant
Knoldus Software LLP
2. 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
3. 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
}
4. 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
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 => ?
6. 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
7. 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.
8. 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
}
}
9. 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 ?
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"
19. val vs var
Variable type Collection type Example
Immutable Immutable Best
val map = Map("name" -> "sky")
20. val vs var
Variable type Collection type Example
Immutable Immutable Best
Mutable Immutable Good
val map = Map("name" -> "sky")
var map = Map("name" -> "sky")
21. 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")
22. 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")
23. 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
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
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)
}
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
val optionalInt:Option[Int]=Some(1)
val intValue= optionalInt.getOrElse("0")
33. getOrElse vs fold
val optionalInt:Option[Int]=Some(1)
val intValue= optionalInt.getOrElse("0")
String value
34. 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)
35. 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
36. 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
38. 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
39. 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)
40. 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
41. 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
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 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] = ???
46. 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
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
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
)
)
}
}
50. 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())
}
}
51. 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
}
}
}
}
53. 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
}
54. 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
}
55. Thump Rule
● In Scala, use Scala ExecutionContext
● In Play, use Play ExecutionContext
● In Akka, use Akka ExecutionContext
● In Akka, One ActorSystem per Application