Options and Exceptions in Scala
November 26, 2019 Leave a comment
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
Recent Comments