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)
    def add4 = superAdder2(20)
    println(add4(7))

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
}
println("Scala Doubler: "+scalaDoubler(5))

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
println(scalaDoubler1(5))

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.

Scala Method Notations

Infix Notation/Operator Notation
Method with one parameter can be accessed with space (Natural Language)

class Person {
  def sayHello(name: String) = println(s"Hello $name")
}
val person = new Person
person sayHello "Niranjan"

In scala we can also use +/-/*/#/$ as method names which makes it much more comfortable to code with unlike other languages. This is exactly what is happening in mathematical operators as they are also methods

    println(1 + 3)
    // ALL OPERATORS ARE METHODS in SCALA
    println(1.+(3))

PREFIX NOTATION or Unary Operator
A Unary Operator is also a METHOD in SCALA
All UNARY Operators are methods with prefix “unary_”

val x = -1
// Its same as
val y = 1.unary_-

POSTFIX Notation
Method with no parameter can be accessed without paranthesis

class Person {
  def sayHello = println("Hello")
}
val person = new Person
person.sayHello
// OR
person sayHello

Classes, Instances, Objects, Companion Object, Scala Application

Class in scala is used to wrap instance level functionality.

class Foo //class

Instance

val foo = new Foo // instance

Note: In scala, we do not call instance of a class as an Object because Object has a special meaning.

Object Scala Object is a Singleton Instance by definition.
SCALA DOES NOT HAVE CLASS LEVEL FUNCTIONALITY (like “static” in java) – Equivalet in scala is Object
Everytime an object is defined, this object will be of its own type (“type person”) and it itself is the only instance.
Objects can be defined in a similar way to that of the classes with the only difference being the constructor parameters.

object Person {
  val N_HANDS = 2 // static in java
}
val person1 = Person // refers to the only instance on Object
val person2 = Person
println(person1 == person2) // returns true

Companion Object
We can define class with the same name as that of object to seperate instance level functionality (Class) from static/class level functionality (Object), and when class and object exists with same name in one file, they are called COMPANION OBJECTS or COMPANIONS

object Person {
  val N_HANDS = 2 // static in java
}
class Person(name: String, val age: Int) {
  // where as name and age are instance specific and hence they are referred in class members
}

Note:
In Java we access static member via class name and instance member via object reference
But in Scala, we access class level memebers via Object and instance level members via the instance of a Class, It turns out to be more Object Oriented than JAVA eventhough its created for functional programming

Scala Application
When a scala object inherts scala.App trait it becomes scala application. The App trait can be used to quickly turn objects into executable programs.

object Main extends App {
  Console.println("Hello World: " + (args mkString ", "))
}
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

%d bloggers like this: