Option, Either, Try
and what to do with corner cases when they arise
Know Your Library mini-series
By Michal Bigos / @teliatko

## Know Your Library - mini series
1. Hands-on with types from Scala library
2. DO's and DON'Ts
3. Intended for rookies, but Scala basic syntax assumed
4. Real world use cases
## Why null isn't an Option
#### Consider following code
```java
String foo = request.params("foo")
if (foo != null) {
String bar = request.params("bar")
if (bar != null) {
doSomething(foo, bar)
} else {
throw new ApplicationException("Bar not found")
}
} else {
throw new ApplicationException("Foo not found")
}
```
## Why null isn't an Option
#### What's wrong with null
```java
/* 1. Nobody knows about null, not even compiler */
String foo = request.params("foo")
/* 2. Annoying checking */
if (foo != null) {
String bar = request.params("bar")
// if (bar != null) {
/* 3. Danger of infamous NullPointerException,
everbody can forget some check */
doSomething(foo, bar)
// } else {
/* 4. Optionated detailed failures,
sometimes failure in the end is enough */
// throw new ApplicationException("Bar not found")
// }
} else {
/* 5. Design flaw, just original exception replacement */
throw new ApplicationException("Foo not found")
}
```
## Dealing with non-existence
#### Different approaches compared
* Java relies on sad `null`
* Groovy provides null-safe operator for accessing properties
```groovy
foo?.bar?.baz
```
* Clojure uses `nil` which is okay very often, but sometimes it leads to an exception higher in call hierarchy
## Getting rid of null
### Non-existence Scala way
```scala
sealed abstract class Option[A]
case class Some[+A](x: A) extends Option[A]
case object None extends Option[Nothing]
```
Container with one or none element
## Option
1. States that value may or may not be present on __type level__
2. You are __forced by the compiler__ to deal with it
3. No way to __accidentally rely__ on presence of a value
4. __Clearly documents__ an intention
## Option
#### Creating an Option
```scala
val certain = Some("Sun comes up")
val pitty = None
```
Never do this
```scala
val nonSense = Some(null)
```
Rather use factory method on companion object
```scala
val muchBetter = Option(null) // Results to None
val certainAgain = Option("Sun comes up") // Some(Sun comes up)
```
## Option
#### Working with Option an old way
```scala
// Assume that
def param[String](name: String): Option[String] ...
val fooParam = request.param("foo")
val foo = if (fooParam.isDefined) {
fooParam.get // throws NoSuchElementException when None
} else {
"Default foo" // Default value
}
```
Don't do this (only in exceptional cases)
## Option
#### Pattern matching
```scala
val foo = request.param("foo") match {
case Some(value) => value
case None => "Default foo" // Default value
}
```
Don't do this (there's a better way)
## Option
#### Providing a default value
```scala
// any long computation for default value
val foo = request.param("foo") getOrElse ("Default foo")
```
Default value is __by-name__ parameter. It's evaluated lazily.
## Option
#### Treating it functional way
Think of Option as collection
It is __biased__ towards `Some`
You can `map`, `flatMap` or compose Option(s) when it contains value, i.e. it's `Some`
## Option
#### Example
Suppose following model and DAO
```scala
case class User(id: Int, name: String, age: Option[Int])
// In domain model, any optional value has to be expressed with Option
object UserDao {
def findById(id: Int): Option[User] = ...
// Id can always be incorrect, e.g. it's possible that user does not exist already
}
```
## Option
#### Side-effecting
Use case: Printing the user name
```scala
// Suppose we have an userId from somewhere
val userOpt = UserDao.findById(userId)
// Just print user name
userOpt.foreach { user =>
println(user.name) // Nothing will be printed when None
} // Result is Unit (like void in Java)
// Or more concise
userOpt.foreach( user => println(user) )
// Or even more
userOpt.foreach( println(_) )
userOpt.foreach( println )
```
## Option
#### map, flatMap & co.
Use case: Extracting age
```scala
// Extracting age
val ageOpt = UserDao.findById(userId).map( _.age )
// Returns Option[Option[Int]]
val ageOpt = UserDao.findById(userId).map( _.age.map( age => age ) )
// ReturnsOption[Option[Int]] too
// Extracting age, take 2
val ageOpt = UserDao.findById(userId).flatMap( _.age.map( age => age ) )
// Returns Option[Int]
```
## Option
#### For comprehensions
Same use case as before
```scala
// Extracting age, take 3
val ageOpt = for {
user <- UserDao.findById(userId)
age <- user.age
} yield age // Returns Option[Int]
```
Usage in left side of generator
```scala
// Extracting age, take 3
val ageOpt = for {
User(_, Some(age)) <- UserDao.findById(userId)
} yield age // Returns Option[Int]
```
## Option
#### Composing to List
Use case: Pretty-print of user
```scala
def prettyPrint(user: User) =
List(Option(user.name), user.age).mkString(", ")
```
Different notation
```scala
def prettyPrint(user: User) =
(Option(user.name) ++ user.age).mkString(", ")
```
Both prints
```scala
val foo = User("Foo", Some(10))
val bar = User("Bar", None)
prettyPrint(foo) // Prints "Foo, 10"
prettyPrint(bar) // Prints "Bar"
```
Rule of thumb: wrap all mandatory fields with Option and then concatenate with optional ones
## Option
#### Chaining
Use case: Fetching or creating the user
```scala
object UserDao {
// New method
def createUser: User
}
val userOpt = UserDao.findById(userId) orElse Some(UserDao.create)
```
More appropriate, when `User` is desired directly
```scala
val user = UserDao.findById(userId) getOrElse UserDao.create
```
## Option
#### More to explore
```scala
sealed abstract class Option[A] {
def fold[B](ifEmpty: ⇒ B)(f: (A) ⇒ B): B
def filter(p: (A) ⇒ Boolean): Option[A]
def exists(p: (A) ⇒ Boolean): Boolean
...
}
```
## Is Option appropriate?
Consider following piece of code
```scala
case class UserFilter(name: String, age: Int)
def parseFilter(input: String): Option[UserFilter] = {
for {
name <- parseName(input)
age <- parseAge(input)
} yield UserFilter(name, age)
}
```
When something went wrong, cause is lost forever
```scala
// Suppose that parseName and parseAge throws FilterException
def parseFilter(input: String): Option[UserFilter]
throws FilterException { ... }
// caller side
val filter = try {
parseFilter(input)
} catch {
case e: FilterException => whatToDoInTheMiddleOfTheCode(e)
}
```
Exception doesn't help much. It only introduces overhead
## Introducing Either
```scala
sealed abstract class Either[+L, +R]
case class Left[+L, +R](a: L) extends Either[L, R]
case class Right[+L, +R](b: R) extends Either[L, R]
```
Container with disjoint types.
## Either
1. States that value is either `Left[L]` or `Right[R]`, but never both.
2. No explicit sematics, but by convention `Left[L]` represents corner case and `Right[R]` desired one.
3. Functional way of dealing with alternatives, consider:
```scala
def doSomething(): Int throws SomeException
// what is this saying? two possible outcomes
def doSomething(): Either[SomeException, Int]
// more functional only one return value
```
4. Again, it __clearly documents__ an intention
## Either
#### Creating Either
```scala
def parseAge(input: String): Either[String, Int] = {
try {
Right(input.toInt)
} catch {
case nfe: NumberFormatException => Left("Unable to parse age")
}
}
```
There is no `Either(...)` factory method on companion object.
## Either
#### Working an old way again
```scala
def parseFilter(input: String): Either[String, ExtendedFilter] = {
val name = parseName(input)
if (name.isRight) {
val age = parseAge(input)
if (age.isRight) {
Right(UserFilter(time, rating))
} else age
} else name
}
```
Don't do this (only in exceptional cases)
## Either
#### Pattern matching
```scala
def parseFilter(input: String): Either[String, ExtendedFilter] = {
parseName(input) match {
case Right(name) => parseAge(input) match {
case Right(age) => UserFilter(name, age)
case error: Left[_] => error
}
case error: Left[_] => error
}
}
```
Don't do this (there's a better way)
## Either
#### Projections
You cannot directly use instance of `Either` as collection.
It's unbiased, you have to define what is your prefered side.
```scala
def parseFilter(input: String): Either[String, UserFilter] = {
for {
name <- parseName(input).right
age <- parseAge(input).right
} yield Right(UserFilter(name, age))
}
```
Working on success, only 1st error is returned.
`either.right` returns `RightProjection`
## Either
#### Projections, take 2
```scala
def parseFilter(input: String): Either[List[String], UserFilter] = {
val name = parseName(input)
val age = parseAge(input)
val errors = name.left.toOption ++ age.left.toOption
if (errors.isEmpty) {
Right(UserFilter(name.right.get, age.right.get))
} else {
Left(errors)
}
}
```
Working on both sides, all errors are collected.
`either.left` returns `LeftProjection`
## Either
#### Projections, take 3
Both projection are biased wrappers for `Either`
You can use `map`, `flatMap` on them too, but beware
```scala
val rightThing = Right(User("Foo", Some(10)))
val projection = rightThing.right // Type is RightProjection[User]
val rightThingAgain = projection.map ( _.name )
// Isn't RightProjection[User] but Right[User]
```
This is inconsistent in regdard to other collections.
## Either
#### Projections, take 4
It can lead to problems with for comprehensions.
```scala
for {
name <- parseName(input).right
bigName <- name.capitalize
} yield bigName
```
This won't compile.
After removing syntactic suggar, we get
```scala
parseName(input).right.map { name =>
val bigName = name.capitalize
(bigName)
}.map { case (x) => x } // Map is not member of Either
```
We need projection again
```scala
for {
name <- parseName(input).right
bigName <- Right(name.capitalize).right
} yield bigName
```
## Either
#### Folding
Allows transforming the `Either` regardless if it's `Right` or `Left` on the same type
```scala
// Once upon a time in controller
parseFilter(input).fold(
// Bad (Left) side transformation to HttpResponse
errors => BadRequest("Error in filter")
// Good (Right) side transformation to HttpResponse
filter => Ok(doSomethingWith(filter))
)
```
Accepts functions, both are evaluated lazily.
Result from both functions has same type.
## Either
#### More to explore
```scala
sealed abstract class Either[+A, +B] {
def joinLeft[A1 >: A, B1 >: B, C](implicit ev: <:<[A1, Either[C, B1]]): Either[C, B1]
def joinRight[A1 >: A, B1 >: B, C](implicit ev: <:<[B1, Either[A1, C]]): Either[A1, C]
def swap: Product with Serializable with Either[B, A]
}
```
## Throwing and catching exceptions
### Sometimes things really go wrong
```scala
def parseAge(input: String): Either[String, Int] = {
try {
Right(input.toInt)
} catch {
case nfe: NumberFormatException => Left("Unable to parse age")
}
}
```
You can use classic `try/catch/finally` construct
## Throwing and catching exceptions
### Sometimes things really go wrong, take 2
But, it's `try/catch/finally` on steroids thanks to pattern matching
```scala
try {
someHorribleCodeHere()
} catch {
// Catching multiple types
case e @ (_: IOException | _: NastyExpception) => cleanUpMess()
// Catching exceptions by message
case e : AnotherNastyException
if e.getMessage contains "Wrong again" => cleanUpMess()
// Catching all exceptions
case e: Exception => cleanUpMess()
}
```
## Throwing and catching exceptions
### Sometimes things really go wrong, take 3
It's powerful, but beware
```scala
try {
someHorribleCodeHere()
} catch {
// This will match scala.util.control.ControlThrowable too
case _ => cleanUpMess()
}
```
Never do this!
```scala
try {
someHorribleCodeHere()
} catch {
// This will match scala.util.control.ControlThrowable too
case t: ControlThrowable => throw t
case _ => cleanUpMess()
}
```
Prefered approach of catching all
## What's wrong with exceptions
1. Referential transparency - is there a value the RHS can be replaced with? No.
```scala
val something = throw new IllegalArgumentException("Foo is missing")
// Result type is Nothing
```
2. Code base can become ugly
3. Exceptions do not go well with concurrency
## Should I throw an exception?
No, there is better approach
## Exception handling functional way
Please welcome
`import scala.util.control._`
and
```scala
sealed trait Try[A]
case class Failure[A](e: Throwable) extends Try[A]
case class Success[A](value: A) extends Try[A]
```
Collection of `Throwable` or value
## Try
1. States that computation may be `Success[T]` or may be `Failure[T]` ending with `Throwable` on type level
2. Similar to `Option`, it's `Success` biased
3. It's `try/catch` without boilerplate
4. Again it clearly documents what is happening
## Try
#### Like Option
All the operations from `Option` are present
```scala
sealed abstract class Try[+T] {
// Throws exception of Failure or return value of Success
def get: T
// Old way checks
def isFailure: Boolean
def isSuccess: Boolean
// map, flatMap & Co.
def map[U](f: (T) ⇒ U): Try[U]
def flatMap[U](f: (T) ⇒ Try[U]): Try[U]
// Side effecting
def foreach[U](f: (T) ⇒ U): Unit
// Default value
def getOrElse[U >: T](default: ⇒ U): U
// Chaining
def orElse[U >: T](default: ⇒ Try[U]): Try[U]
}
```
## Try
#### But there is more
Assume that
```scala
def parseAge(input: String): Try[Int] = Try ( input.toInt )
```
Recovering from a Failure
```scala
val age = parseAge("not a number") recover {
case e: NumberFormatException => 0 // Default value
case _ => -1 // Another default value
} // Result is always Success
```
Converting to `Option`
```scala
val ageOpt = age.toOption
// Will be Some if Success, None if Failure
```
## scala.util.control._
1. Utility methods for common exception handling patterns
2. Less boiler plate than `try/catch/finally`
## scala.util.control._
#### Catching an exception
```scala
catching(classOf[NumberFormatException]) {
input.toInt
} // Returns Catch[Int]
```
It returns `Catch[T]`
## scala.util.control._
#### Converting
Converting to `Option
```scala
catching(classOf[NumberFormatException]).opt {
input.toInt
} // Returns Option[Int]
```
```scala
failing(classOf[NumberFormatException]) {
input.toInt
} // Returns Option[Int]
```
Converting to `Either`
```scala
catching(classOf[NumberFormatException]).either {
input.toInt
} // Returns Either[Throwable, Int]
```
Converting to `Try`
```scala
catching(classOf[NumberFormatException]).withTry {
input.toInt
} // Returns Try[Int]
```
## scala.util.control._
#### Side-effecting
```scala
ignoring(classOf[NumberFormatException]) {
println(input.toInt)
} // Returns Catch[Unit]
```
## scala.util.control._
#### Catching non-fatal exceptions
```scala
nonFatalCatch {
println(input.toInt)
}
```
What are non-fatal exceptions?
All instead of:
`VirtualMachineError`, `ThreadDeath`, `InterruptedException`, `LinkageError`, `ControlThrowable`, `NotImplementedError`
## scala.util.control._
#### Providing default value
```scala
val age = failAsValue(classOf[NumberFormatException])(0) {
input.toInt
}
```
## scala.util.control._
#### What about finally
With catch logic
```scala
catching(classOf[NumberFormatException]).andFinally {
println("Age parsed somehow")
}.apply {
input.toInt
}
```
No catch logic
```scala
ultimately(println("Age parsed somehow")) {
input.toInt
}
```
## scala.util.control._
There's more to cover and explore,
please check out the [Scala documentation](http://docs.scala-lang.org).
Thanks for your attention