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