option, either, try and what to do with corner cases when they arise
DESCRIPTION
Part of mini-series of talks about gems in Scala standard library. Used for education of junior developers in our company. This pare is about Option, Either, Try and error handling in Scala in general.TRANSCRIPT
![Page 1: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/1.jpg)
OPTION, EITHER, TRYAND WHAT TO DO WITH CORNER CASES WHEN
THEY ARISEKNOW YOUR LIBRARY MINI-SERIES
By /
Michal Bigos @teliatko
![Page 2: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/2.jpg)
KNOW YOUR LIBRARY - MINI SERIES1. Hands-on with types from Scala library2. DO's and DON'Ts3. Intended for rookies, but Scala basic syntax assumed4. Real world use cases
![Page 3: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/3.jpg)
WHY NULL ISN'T AN OPTIONCONSIDER FOLLOWING CODE
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")}
![Page 4: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/4.jpg)
WHY NULL ISN'T AN OPTIONWHAT'S WRONG WITH NULL
/* 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") }
![Page 5: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/5.jpg)
DEALING WITH NON-EXISTENCEDIFFERENT APPROACHES COMPARED
Java relies on sad nullGroovy provides null-safe operator for accessingproperties
Clojure uses nil which is okay very often, but sometimesit leads to an exception higher in call hierarchy
foo?.bar?.baz
![Page 6: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/6.jpg)
GETTING RID OF NULLNON-EXISTENCE SCALA WAY
Container with one or none element
sealed abstract class Option[A]
case class Some[+A](x: A) extends Option[A]
case object None extends Option[Nothing]
![Page 7: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/7.jpg)
OPTION1. 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
![Page 8: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/8.jpg)
OPTION IS MANDARORY!
![Page 9: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/9.jpg)
OPTIONCREATING AN OPTION
Never do this
Rather use factory method on companion object
val certain = Some("Sun comes up")val pitty = None
val nonSense = Some(null)
val muchBetter = Option(null) // Results to Noneval certainAgain = Option("Sun comes up") // Some(Sun comes up)
![Page 10: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/10.jpg)
OPTIONWORKING WITH OPTION AN OLD WAY
Don't do this (only in exceptional cases)
// Assume thatdef 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}
![Page 11: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/11.jpg)
OPTIONPATTERN MATCHING
Don't do this (there's a better way)
val foo = request.param("foo") match { case Some(value) => value case None => "Default foo" // Default value}
![Page 12: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/12.jpg)
OPTIONPROVIDING A DEFAULT VALUE
Default value is by-name parameter. It's evaluated lazily.
// any long computation for default valueval foo = request.param("foo") getOrElse ("Default foo")
![Page 13: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/13.jpg)
OPTIONTREATING IT FUNCTIONAL WAY
Think of Option as collection
It is biased towards Some
You can map, flatMap or compose Option(s) when itcontains value, i.e. it's Some
![Page 14: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/14.jpg)
OPTIONEXAMPLE
Suppose following model and DAOcase 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}
![Page 15: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/15.jpg)
OPTIONSIDE-EFFECTING
Use case: Printing the user name// Suppose we have an userId from somewhereval userOpt = UserDao.findById(userId)
// Just print user nameuserOpt.foreach { user => println(user.name) // Nothing will be printed when None} // Result is Unit (like void in Java)
// Or more conciseuserOpt.foreach( user => println(user) ) // Or even more userOpt.foreach( println(_) )userOpt.foreach( println )
![Page 16: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/16.jpg)
OPTIONMAP, FLATMAP & CO.
Use case: Extracting age// Extracting ageval 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 2val ageOpt = UserDao.findById(userId).flatMap( _.age.map( age => age ) ) // Returns Option[Int]
![Page 17: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/17.jpg)
OPTIONFOR COMPREHENSIONS
Same use case as before
Usage in left side of generator
// Extracting age, take 3val ageOpt = for { user <- UserDao.findById(userId) age <- user.age} yield age // Returns Option[Int]
// Extracting age, take 3val ageOpt = for { User(_, Some(age)) <- UserDao.findById(userId)} yield age // Returns Option[Int]
![Page 18: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/18.jpg)
OPTIONCOMPOSING TO LIST
Use case: Pretty-print of user
Different notation
Both prints
Rule of thumb: wrap all mandatory fields with Option andthen concatenate with optional ones
def prettyPrint(user: User) = List(Option(user.name), user.age).mkString(", ")
def prettyPrint(user: User) = (Option(user.name) ++ user.age).mkString(", ")
val foo = User("Foo", Some(10))val bar = User("Bar", None)
prettyPrint(foo) // Prints "Foo, 10"prettyPrint(bar) // Prints "Bar"
![Page 19: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/19.jpg)
![Page 20: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/20.jpg)
OPTIONCHAINING
Use case: Fetching or creating the user
More appropriate, when User is desired directly
object UserDao { // New method def createUser: User}
val userOpt = UserDao.findById(userId) orElse Some(UserDao.create)
val user = UserDao.findById(userId) getOrElse UserDao.create
![Page 21: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/21.jpg)
OPTIONMORE TO EXPLORE
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 ...}
![Page 22: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/22.jpg)
IS OPTION APPROPRIATE?Consider following piece of code
When something went wrong, cause is lost forever
case class UserFilter(name: String, age: Int)
def parseFilter(input: String): Option[UserFilter] = { for { name <- parseName(input) age <- parseAge(input) } yield UserFilter(name, age)}
// Suppose that parseName and parseAge throws FilterExceptiondef parseFilter(input: String): Option[UserFilter] throws FilterException { ... }
// caller sideval filter = try { parseFilter(input)} catch { case e: FilterException => whatToDoInTheMiddleOfTheCode(e)}
![Page 23: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/23.jpg)
Exception doesn't help much. It only introduces overhead
![Page 24: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/24.jpg)
INTRODUCING EITHER
Container with disjoint types.
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]
![Page 25: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/25.jpg)
EITHER1. 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:
4. Again, it clearly documents an intention
def doSomething(): Int throws SomeException // what is this saying? two possible outcomes def doSomething(): Either[SomeException, Int]// more functional only one return value
![Page 26: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/26.jpg)
EITHER IS NOT BIASED
![Page 27: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/27.jpg)
EITHERCREATING EITHER
There is no Either(...) factory method on companionobject.
def parseAge(input: String): Either[String, Int] = { try { Right(input.toInt) } catch { case nfe: NumberFormatException => Left("Unable to parse age") }}
![Page 28: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/28.jpg)
EITHERWORKING AN OLD WAY AGAIN
Don't do this (only in exceptional cases)
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}
![Page 29: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/29.jpg)
EITHERPATTERN MATCHING
Don't do this (there's a better way)
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 }}
![Page 30: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/30.jpg)
EITHERPROJECTIONS
You cannot directly use instance of Either as collection.
It's unbiased, you have to define what is your prefered side.
Working on success, only 1st error is returned.
either.right returns RightProjection
def parseFilter(input: String): Either[String, UserFilter] = { for { name <- parseName(input).right age <- parseAge(input).right } yield Right(UserFilter(name, age))}
![Page 31: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/31.jpg)
EITHERPROJECTIONS, TAKE 2
Working on both sides, all errors are collected.
either.left returns LeftProjection
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) }}
![Page 32: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/32.jpg)
EITHERPROJECTIONS, TAKE 3
Both projection are biased wrappers for Either
You can use map, flatMap on them too, but beware
This is inconsistent in regdard to other collections.
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]
![Page 33: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/33.jpg)
EITHERPROJECTIONS, TAKE 4
It can lead to problems with for comprehensions.
This won't compile.
After removing syntactic suggar, we get
We need projection again
for { name <- parseName(input).right bigName <- name.capitalize} yield bigName
parseName(input).right.map { name => val bigName = name.capitalize (bigName)}.map { case (x) => x } // Map is not member of Either
![Page 34: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/34.jpg)
for { name <- parseName(input).right bigName <- Right(name.capitalize).right} yield bigName
![Page 35: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/35.jpg)
EITHERFOLDING
Allows transforming the Either regardless if it's Right orLeft on the same type
Accepts functions, both are evaluated lazily. Result from bothfunctions has same type.
// Once upon a time in controllerparseFilter(input).fold( // Bad (Left) side transformation to HttpResponse errors => BadRequest("Error in filter") // Good (Right) side transformation to HttpResponse filter => Ok(doSomethingWith(filter)))
![Page 36: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/36.jpg)
EITHERMORE TO EXPLORE
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]}
![Page 37: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/37.jpg)
THROWING AND CATCHING EXCEPTIONSSOMETIMES THINGS REALLY GO WRONG
You can use classic try/catch/finally construct
def parseAge(input: String): Either[String, Int] = { try { Right(input.toInt) } catch { case nfe: NumberFormatException => Left("Unable to parse age") }}
![Page 38: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/38.jpg)
THROWING AND CATCHING EXCEPTIONSSOMETIMES THINGS REALLY GO WRONG, TAKE 2
But, it's try/catch/finally on steroids thanks to patternmatching
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()}
![Page 39: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/39.jpg)
THROWING AND CATCHING EXCEPTIONSSOMETIMES THINGS REALLY GO WRONG, TAKE 3
It's powerful, but beware
Never do this!
Prefered approach of catching all
try { someHorribleCodeHere()} catch { // This will match scala.util.control.ControlThrowable too case _ => cleanUpMess()}
try { someHorribleCodeHere()} catch { // This will match scala.util.control.ControlThrowable too case t: ControlThrowable => throw t case _ => cleanUpMess()}
![Page 40: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/40.jpg)
![Page 41: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/41.jpg)
WHAT'S WRONG WITH EXCEPTIONS1. Referential transparency - is there a value the RHS can be
replaced with? No.
2. Code base can become ugly
3. Exceptions do not go well with concurrency
val something = throw new IllegalArgumentException("Foo is missing") // Result type is Nothing
![Page 42: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/42.jpg)
SHOULD I THROW AN EXCEPTION?No, there is better approach
![Page 43: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/43.jpg)
EXCEPTION HANDLING FUNCTIONAL WAYPlease welcome
import scala.util.control._
and
Collection of Throwable or value
sealed trait Try[A]
case class Failure[A](e: Throwable) extends Try[A]
case class Success[A](value: A) extends Try[A]
![Page 44: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/44.jpg)
TRY1. States that computation may be Success[T] or may beFailure[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
![Page 45: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/45.jpg)
TRYLIKE OPTION
All the operations from Option are presentsealed 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]}
![Page 46: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/46.jpg)
TRYBUT THERE IS MORE
Assume that
Recovering from a Failure
Converting to Option
def parseAge(input: String): Try[Int] = Try ( input.toInt )
val age = parseAge("not a number") recover { case e: NumberFormatException => 0 // Default value case _ => -1 // Another default value} // Result is always Success
val ageOpt = age.toOption // Will be Some if Success, None if Failure
![Page 47: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/47.jpg)
SCALA.UTIL.CONTROL._1. Utility methods for common exception handling patterns
2. Less boiler plate than try/catch/finally
![Page 48: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/48.jpg)
SCALA.UTIL.CONTROL._CATCHING AN EXCEPTION
It returns Catch[T]
catching(classOf[NumberFormatException]) { input.toInt} // Returns Catch[Int]
![Page 49: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/49.jpg)
SCALA.UTIL.CONTROL._CONVERTING
Converting to `Option
Converting to Either
Converting to Try
catching(classOf[NumberFormatException]).opt { input.toInt} // Returns Option[Int]
failing(classOf[NumberFormatException]) { input.toInt} // Returns Option[Int]
catching(classOf[NumberFormatException]).either { input.toInt} // Returns Either[Throwable, Int]
catching(classOf[NumberFormatException]).withTry { input.toInt} // Returns Try[Int]
![Page 50: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/50.jpg)
![Page 51: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/51.jpg)
SCALA.UTIL.CONTROL._SIDE-EFFECTING
ignoring(classOf[NumberFormatException]) { println(input.toInt)} // Returns Catch[Unit]
![Page 52: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/52.jpg)
SCALA.UTIL.CONTROL._CATCHING NON-FATAL EXCEPTIONS
What are non-fatal exceptions?
All instead of:
VirtualMachineError, ThreadDeath,InterruptedException, LinkageError,ControlThrowable, NotImplementedError
nonFatalCatch { println(input.toInt)}
![Page 53: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/53.jpg)
SCALA.UTIL.CONTROL._PROVIDING DEFAULT VALUE
val age = failAsValue(classOf[NumberFormatException])(0) { input.toInt}
![Page 54: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/54.jpg)
SCALA.UTIL.CONTROL._WHAT ABOUT FINALLY
With catch logic
No catch logic
catching(classOf[NumberFormatException]).andFinally { println("Age parsed somehow")}.apply { input.toInt}
ultimately(println("Age parsed somehow")) { input.toInt}
![Page 55: Option, Either, Try and what to do with corner cases when they arise](https://reader033.vdocuments.mx/reader033/viewer/2022051815/5406ba298d7f72b90a8b475d/html5/thumbnails/55.jpg)
SCALA.UTIL.CONTROL._There's more to cover and explore,
please check out the .Scala documentation