## Practical Use Cases of Switch Statement in Scala

1. Catch blocks are Pattern Matches and is same as below code

```    try {
// logic
} catch {
case e: RuntimeException => "runtime"
case e: NullPointerException => "npe"
case _ => "something else"
}

// Catch blocks are Pattern Matches and is same as below code
/*
try {
// logic
} catch(e) {
e match {
case e: RuntimeException => "runtime"
case e: NullPointerException => "npe"
case _ => "something else"
}
}
*/
```

2. Generators are also based on pattern matching

```val list = List(1, 2, 3, 4)
val evenOnes = for {
x <- list if x % 2 == 0 // GENERATOR
} yield x
println(evenOnes)

val tuples = List((1, 2), (2, 3))
val filterTuples = for {
(first, second) <- tuples // extractor in this generator
} yield first * second
println(filterTuples)
```

3. Multi value definitons are based on PMs

```val tuple = (1, 2, 3)
val (a, b, c) = tuple
println(b)

val head :: tail = list
println(tail)
```

4. Partial Functions are based on Pattern Matching

```    val mappedList = list.map {
case v if v % 2 == 0 => s"\$v is even"
case 1 => "One"
case _ => "Something else "
}
println(mappedList)
// Note: Partial Function is a literal inside {}

// is same as
val mappedList2 = list.map(x => x match {
case v if v % 2 == 0 => s"\$v is even"
case 1 => "One"
case _ => "Something else "
})
```

## Switch Cases and Pattern Matching in Scala

In scala switch is enabled via Pattern Matching (PM) and its much more powerful than other languages often referred as “Switch on Steroids”
Some of the properties of switch case are
1. Cases are match in the same order they are written
2. If no cases match it will result in MatchError, make sure all the cases are covered with wildcard
3. Type of Pattern Matching expression is the unified type of all the types from all the cases
4. PM works really well with case classes

Simple switch statement

```val random = new Random
val x = random.nextInt(10)

val description = x match {
case 1 => "First element"
case 2 => "Second element"
case 3 => "Third element"
case _ => "Default element" // this is a WILDCARD which matches for all other patterns
}
println(description)
```

Interesting property of Pattern Matching is that it can decompose values especially when used with case classes.

```sealed class Animal
case class Dog(breed: String) extends Animal
case class Parrot(greeting: String) extends Animal

val animal: Animal = Dog("terra Nova")
animal match {
case Dog(breed) => println(s"Matched dog of type \$breed breed")
}
```

Different scenarios/ways we can use switch statement

```    // 1. Constants
val x: Any = "Scala"

val constants: Any = x match {
case 1 => "a number"
case "Scala" => "The scala"
case true => "The Truth"
case a02AllThePatterns => "A singleton Object"
}
println(constants)

// 2. Match Anything
// 2.1 Wildcard
val matchAnything = x match {
case _ => "I will match everything"
}
println(matchAnything)

// 2.2 variable
val matchAVariable = x match {
case something => s"I have found \$something"
}
println(matchAVariable)

// 3 tuples
val aTuple = (1, 2)
val matchATuple = aTuple match {
case (1, 1) => "Found tuple"
case (something, 2) => s"I have found tuple with \$something"
}
println(matchATuple)

// PMs can be nested
val nestedTuple = (1, (2, 3))
val matchANestedTuple = nestedTuple match {
case (_, (2, v)) =>
}
println(matchANestedTuple)

// 4. case classes - constructor pattern
// PMs can be nested with CCs as well
val aList: GenericList[Int] = new Cons(1, new Cons(2, Empty))
val matchAList = aList match {
case Empty =>
case Cons(h, t) =>
case Cons(h, Cons(subHead, subTail)) =>  // nested classes
}

// 5 - List Patterns
val aStandardList = List(1, 2, 3, 4, 5)
val matchAStandardList = aStandardList match {
case List(1, _, _, _) => // extractor concept in advanced training
case List(1, _*) => // List of arbitrary length in advanced training
case 1 :: List(_) => // infix pattern
case List(1, 2, 3) :+ 42 => // infix pattern
}

// 6. Type specifiers
val unknown: Any = 2
val matchAUnknown = unknown match {
case list: List[Int] => // explicit type specifier
case _ =>
}

// 7. Name Binding
val MatchANameBinding = aList match {
case nonEmptyList @ Cons(_, _) =>
// name binding allows us to use the name later
// Its like variables but we can name entire pattern here and its very powerful
case Cons(1, rest @ Cons(2, _)) =>
// Name Binding is also possible in nested patterns
}

// 8. Multi Patterns
val matchAMultiPattern = aList match {
case Empty | Cons(0, _) => // Matches multiple patterns with pipe operator
}

// 9. if guards
val matchASpecialElement = aList match {
case Cons(specialElement, _) if specialElement % 2 == 0 =>
// if guard on a variable
}
```

## Options and Exceptions in Scala

Option is solution to a Billion Dollar Mistake introduced by Null Pointer Exceptions. Options deal with unsafe APIs and never do null checks.
As .get is a unsafe operation, we should rely on map/flatMap to read the values from options.
Since we can use map, flatMap on options, we can also use for comprehension.

Scala Implementation Classes

```        sealed abstract class Option[+A]
case class Some[+A](x: A) extends Option[A]
case object None extends Option[Nothing]
```

Code Sample

```     val config: Map[String, String] = Map(
"age" -> "36",
"gender" -> "Male"
)

class Person {
def getPerson = println("person created")
}
object Person {
val random = new Random(System.nanoTime())

def apply(age: String, gender: String): Option[Person] =
if(random.nextBoolean()) Some(new Person)
else None
}

// by default .get on map returns option
val age = config.get("age")
val gender = config.get("gender")
// NOTE: age.get is unsafe operation and hence we should only rely on map/flatMap to get the value from options
/*
if (age != null && gender != null)
return Person(age, gender)
else
return null
*/
val person = age.flatMap(a => gender.flatMap(g => Person(a, g)))
/*
if (person != null)
return person.getPerson
else
return null
*/
val personStatus = person.map(c => c.getPerson)
/*
Same as
if (status != null)
println(status)
*/
personStatus.foreach(println)

// shorthand chained solution
config.get("age")
.flatMap(h => config.get("gender")
.flatMap(p => Person(h, p))
.map(c => c.connect))
.foreach(println)

// for comprehensions: Instead of using chained or multiple map/flatmaps we can use this for readability (much widely used and more readable)
// If any of the the for statements in None, then entire for-yield will return None
val forPersonStatus = for {
age <- config.get("age")
gender <- config.get("gender")
Person <- Person(age, gender)
} yield Person.connect

forPersonStatus.foreach(println)
```

Exceptions
In imperative languages like Java, as number of try blocks increases, program becomes barely readable
Scala provides a way to avoid countless try-catch blocks

Scala Implementation Classes

```        sealed abstract class Try[+T]
case class Failure[+T](t: Throwable) extends Try[T]
case class Success[+T](value: T) extends Try[T]
```

Code Sample

```     val aSuccess = Success(3)
val aFailure = Failure(new RuntimeException("Not valid"))
println(aSuccess)
println(aFailure)

// unsafe methods
def unsafeMethod = throw new RuntimeException("Unsafe method invoked")
// Try with apply method is going to catch the exception and wrap it in Failure object
val potentialFailure = Try(unsafeMethod)
println(potentialFailure)
println(potentialFailure.isSuccess) // tells whether exception is thrown or not

// orElse
def backUpMethod(): String = "A valid result"
val fallbackTry = Try(unsafeMethod).orElse(Try(backUpMethod))
println(fallbackTry)

// design the apis in better way
def betterUnsafeMethod: Try[String] = Failure(new RuntimeException("Better Unsafe method"))
def betterBackupMethod: Try[String] = Success("Better valid result")
val betterFallback = betterUnsafeMethod orElse betterBackupMethod
println(betterFallback)

// map, flatMap, filter
println(aSuccess.map(_ * 3))
println(aSuccess.flatMap(x => Success(x * 10)))
println(aSuccess.filter(_ > 10)) // this will turn success into failure

// since map, flatMap, filter are available so will be for-comprehension
```

## Collections, Tuples and Maps

Traverable: It is the root trait of all collections and offers very important methods
maps: maps, flatMap, collect
conversions: toArray, toList, toSeq
size info: isEmpty, size, nonEmpty
tests: exists, forAll
folds: foldLeft, foldRight, reduceLeft, reduceRight
retrieval: head, find, tail
string ops: mkString

By default we use immutable collections in scala

```package object scala {
type List[+A] = immutable.List[A]
}

object Predef {
type Map[A, +B] = immutable.Map[A, B]
type Set[A] = immutable.Set[A]
}

Note: By default Seq apply method returns list
val aSeq = Seq(1, 3, 4, 2) // returns List
println(aSeq(2)) // overloaded apply method to get value at this index = prints 3
```

Tuples
Tuple is an infinite ordered list. It has copy method similar to case classees.

```    val aTuple = new Tuple2[Int, String](2, "Hello Scala") // Tuples can be extended to 22 different parameters to be in consistent with functions
// is same as
val aTuple1 = Tuple2(2, "Hello Scala")
val aTuple2 = (2, "Hello Scala")
val aTuple3 = (2 -> "Hello Scala")

println(aTuple._1)
println(aTuple.copy(_2 = "Good Bye")) // copy methods similar to case classes
println(aTuple.swap) // swaps key value positions
```

Maps

```    val phoneBook = Map(("Jim", 555), ("Niran" -> 999)).withDefaultValue("-1")
println(phoneBook)
println(phoneBook.contains("Niran"))
println(phoneBook("Mary")) // throws error if key is not found

// add new pairing
val newPairing = ("Mary" -> 909)
val newPhonebook = phoneBook + newPairing
println(newPhonebook)

// filterKeys
println(phoneBook.filterKeys(_.startsWith("N")))
// mapValues
println(phoneBook.mapValues(number => "01245-" + number))
//    println(phoneBook.mapValues(number => number * 10))

// conversions to other collections
println(phoneBook.toList) // list of tuples
println(List(("Niran" -> 57687)).toMap)

val names = List("Niran", "Naveen", "Jim", "John")
println(names.groupBy(_.charAt(0)))
```

## map, flatMap, filter and for comprehension in Scala

.map
When map is applied on some collection of type A, it returns a new collection of the same type with elements of type B.

```    val list = List(1,2,3) // this is calling apply method on companion object
println(list)
// map
println(list.map(_ + 1)) // returns List (same type) of type Int
println(list.map(_ + " is a number")) // returns List (same type) of type String
```

.filter
Takes a lambda that takes one param and returns boolean. Values are filtered when boolean is true.

```  println(list.filter(_ % 2 == 0)) // prints 2
```

.flatMap
flatten (reduces) the hierarchy by one level each time it is applied

```    val toPair = (x: Int) => List(x, x+1)
println(list.flatMap(toPair))
```

Example with both map and flatMap

```val numbers = List(1, 2, 3, 4)
val chars = List('a', 'b', 'c', 'd')
val colors = List("White", "Black")
val combinations = numbers.flatMap(n => chars.flatMap(c => colors.map(co => c + "" + n + " " + co)))
println(combinations)
```

for comprehensions
since the above maps and flatMaps are difficult to read, we use for comprehensions and the compiler will transform the for-comprehension to maps and flatMaps for us

```    val forCombinations = for {
n <- numbers
c <- chars
co <- colors
} yield "" + c + "" + n + " " + co
println(forCombinations)
```

## Higher Order Functions (HOFs) and Currying In Scala

Higher Order Functions This takes functions as input parameters and return another function as an output aka HOFs
Note: map, flatMap, filter belongs to HOFs as they take function as an arg
Example

```val superFunction: (Int, (String, (Int => Boolean)) => Int) => (Int => Int) = null
// Input: (String, (Int => Boolean)) => Int) // function within a function
// Output: (Int => Int) // another function which takes Int and returns Int
```

Currying Functions
Function that applies a given function ‘n’ times over a given value ‘x’

```  // nTimes(F, n, x)
// nTimes(f, 3, x) = f(f(f(x))) = nTimes(f, 2, f(x))

def nTimes(f: (Int => Int), n: Int, x: Int): Int = {
if (n == 0) x
else nTimes(f, n-1, f(x))
}

val adder: Int => Int = _ + 1
val result = nTimes(adder, 10, 0)
println(result)

// better way of writing it is to return a function that can compute the value
// ntb(f, n) = x => f(f(f(....(x))))
def nTimesBetter(f: Int => Int, n: Int): Int => Int = {
if (n == 0) (x: Int) => x
else (x: Int) => nTimesBetter(f, n-1)(f(x))
}
val plus10 = nTimesBetter(adder, 10)
println(plus10(0))
```

Examples on how to consume and return curried functions

```    def toCurry(f: (Int, Int) => Int): (Int => Int => Int) =
x => y => f(x, y)   // lambda taking value 'x', returns a lambda taking value 'y' and the result is f(x, y)
// It receives a function of type (Int, Int) => Int
// It returns a curried function
// Note: Looks simple to read but its very hard to think about

def fromCurry(f: Int => Int => Int): (Int, Int) => Int =
(x, y) => f(x)(y)

// TEST
// def superAdder2: (Int => Int => Int) = toCurry(_ + _) is same as below one
def superAdder2: (Int => Int => Int) = toCurry((x, y) => x + y)
```

Scala supports another kind of curried function by specifying functions with MULTIPLE PARAMETER LISTS
Curried Formats are like PARTIAL FUNCTIONS in JS
Note: For functions with multiple parameters, if we want to define smaller functions later (Line:43) we have to define type on RHS, otherwise it will not compile.

```def curriedFormatter(stringFormat: String)(value: Double): String = stringFormat.format(value)
val standardFormat: (Double => String) = curriedFormatter("%4.2f")
val preciseFormat: (Double => String) = curriedFormatter("%10.8f")
println(standardFormat(Math.PI))
println(preciseFormat(Math.PI))
```

Function Library (Function1 till Function22) has two very important functions that are very useful.

• compose
• fromThen
```    // genericize the above two functions
def compose[A, B, T](f: A => B, g: T => A): T => B = x => f(g(x))
def fromThen[A, B, C](f: A => B, g: B => C): A => C = x => g(f(x))

val add2 = (x: Int) => x + 2
val times3 = (x: Int) => x * 3
val composed = compose(add2, times3)
println(composed(4)) // T=4, A=12, B=14 result is 14
val ordered = fromThen(add2, times3)
println(ordered(4)) // A=4, B=6, C=18 result is 18
```

## Functions in Scala

As scala is functional programming we will be able to use functions as first class citizens (passing functions as arguments, returning functions as return type, assigning functions to variables…)
But the problem is that scala sits on top of JVM and JVM is designed for Object Oriented Programming. Scala solves this problem like this

```trait MyFunction[A, B] {
def apply(element: A): B
}
// Java way of implementing above function aka Anonymous Function
val doubler = new MyFunction[Int, Int] {
// does not need 'override keyword for trait methods'
def apply(e: Int): Int = e * 2
}
println(doubler(5)) // prints 10
```

For this reason, to enable developers make use of functional programming concepts, scala provided pre-defined traits/functions (Function1 till Function22) that can take up to 22 generic parameters.
Function1 takes 1 type as input (input param) and return 1 type as output (return type). For example, we can redefine above implementation like this

```val scalaDoubler = new Function1[Int, Int] {
def apply(a: Int) = a * 2
}
```

Apply Method: This method is very special and allows us to call instances of classes or singleton objects like they were functions. And functions are actually instances of classes with apply method.
Anonymous Functions
In scala we can write anonymous function as LAMBDA.
Same Function1 above can be re-written as below different ways

```val scalaDoubler1 = (a: Int) => a * 2
or
val scalaDoubler1: (Int => Int) = a => a * 2
or
val scalaDoubler1: (Int => Int) = _ * 2
```

## Case Classes in Scala

Case Classes (CCs) are very handy and powerful in scala. These are loaded with lot of important features than normal classes that we may need majority of the times. Some of the features are

1. class parameters are promoted to class fields (no need of explicit val declaration)

```    val p1 = new Person("ntalla", 26)
println(p1.name)
```

2. Sensible toString is implemented

```    println(p1) // gives readable object notation instead of some default hash address
```

3. equals and hashCode implementations: This reason makes case classes particularly important in collections

```    val p2 = new Person("ntalla", 26)
println(p1 == p2) // this will return true
```

4. Handy copy methods

```    val p2Copy = p2.copy(name = "ntallapa")
println(p2Copy)
```

5. CCs have companion objects

```    val thePerson = Person
// this is the companion object that is created by case class
// this system created companion object will also have some handy factory methods
val ntalla = Person("ntalla1", 26) // this is apply method on companion object
// companion apply method does the same thing as that of the class constructor
```

6. CCs are serializable
// this feature makes CCs very useful in distributed systems where objects are sent over the network across JVMs

7. CCs have extractor patterns = CCs can be used in PATTERN MATCHING (another very very powerful feature)

Anonymous Classes
Giving implementation without a name. Anonymous implementation can be done for Normal Classes, Abstract Classes and also Traits

```  abstract class Animal {
def eat: Unit
}

// giving implementation to abstract class without naming it - Anonymous
val funnyAnimal: Animal = new Animal {
override def eat: Unit = println("akdhasks")
}

// proof of instance on abstract class
println(funnyAnimal.getClass)

// We can also create anonymous class for normal classes
// as well along with traits and abstract classes
class Person(name: String) {
def sayHi: String = s"\$name said hello"
}

val p1 = new Person("ntalla") {
override def sayHi: String = s"\${super.sayHi} and welcome"
}
```

## Generics, variance problems and bounded types

In scala, generics work for both classes and traits. We can have any number of generic type arguments.

```class MyList[A] {
def add(element: A): MyList[A] = ???
}
val listOfIntegers = new MyList[Int]
val listOfStrings = new MyList[String]
```

// we can have any number of generic type arguments
class MyMap[K, V]

Variance
It defines inheritance relationships of parameterized types. There are 3 types of variance

```class Animal
class Cat extends Animal
class Dog extends Animal
```

Covariance: List[Cat] can extend from List[Animal]

```    class CovariantList[+A] // Observe the symbol +
val animal: Animal = new Cat
val animalList: CovariantList[Animal] = new CovariantList[Cat]
```

Invariance: List[Cat] cannot extend from List[Animal]

```  class InvarianceList[A]
val invariantAnimalList: InvarianceList[Animal] = new InvarianceList[Animal]
```

Contravariance: List[Animal] can extend from List[Cat]

```    class ContraVariantList[-A]
val contraVariantList: ContraVariantList[Cat] = new ContraVariantList[Animal]
// this may be confusing, look at the below example
class Trainer[-A]
val contraVariantList1: Trainer[Cat] = new Trainer[Animal]
// Now this makes sense as animal trainer can also train cat
```

Bounded Types
These allow us to use generic classes only for certain types either subtype/supertype of certain types

```SubType
class Cage[A <: Animal](animal: A) // it says class Cage accepts
// only of type A that are sub type of Animal
val cage = new Cage(new Dog)
class Car
// val newCage = new Cage(new Car) // compiler does not
// report error but it gets runtime exception
SuperType
// this enforces super type
class Cage1[A >: Animal](animal: A)
```

## Constructors, Inheritance, and Abstraction

Class Parameters vs Class Fields
Parameters passed in the class constructor cannot be accessed on the instance unless they are marked ‘val’ whereas
Fields can be accessed directly on the instance.

```class Person(name: String, val age: Int) {
// instance level functionality
// name is Class Parameter
// age is class Field
}
val person = new Person("ntallapa", 23)
println(person.age) // is valid because age is class field and hence can be accesses directly on the object
println(person.name) // Invalid as parameters cannot be directly accessed
```

Primary Constructor, Secondary/Auxiliary Constructors
Constructor defined inline with the class definition is the primary constructor.
Auxiliary constructors can only invoke primary constructors with some default values.

```class Person(name: String, val age: Int) { // Primary Constructor
def this(name: String) = this(name, 0) // Auxiliary constructor
}
```

If there is is mismatch in number of constructor args between parent and child class, we should mention it explicitly otherwise the JVM default behavior is to look at same number constructor in the parent class

```// constructors
class Person(name: String, age: Int)
// class Adult(name: String, age: Int, idCard: String) extends Person
// the above stmt will not work as there is no 3-arg constructor in Person
class Adult(name: String, age: Int, idCard: String) extends Person(name, age) // is the correct way
```

Auxiliary constructors are really not much useful in Scala.

There are 3 Specifiers (conceptually similar to Java)

• private
• protected
• none (public)

Inheritance
Single inheritance can be achieved via extends keyword (extending classes and abstract classes) and multiple inheritance is achieved via “with” keyword on Traits

Abstract Classes and Traits
Unlike in Java, we can have both abstract and non-abstract members in Abstract Class and also in Trait
Differences
traits cannot have constructor parameters
we can only extend one class but inherit multiple traits
traits are a type of a behavior whereas abstract class is a type of thing

What is a sealed class/trait in scala?

A sealed class/trait can only be extended within the same file and not possible to extent outside of the file.

Mawazo

Mostly technology with occasional sprinkling of other random thoughts

amintabar

Amir Amintabar's personal page

101 Books

Reading my way through Time Magazine's 100 Greatest Novels since 1923 (plus Ulysses)

Seek, Plunnge and more...

My words, my world...

ARRM Foundation

Do not wait for leaders; do it alone, person to person - Mother Teresa

Executive Management

An unexamined life is not worth living – Socrates

javaproffesionals

A topnotch WordPress.com site

thehandwritinganalyst

Just another WordPress.com site

coding algorithms

"An approximate answer to the right problem is worth a good deal more than an exact answer to an approximate problem." -- John Tukey