SlideShare une entreprise Scribd logo
1  sur  56
Télécharger pour lire hors ligne
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
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
/* 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
Clojure uses nilwhich is okay very often, but sometimes
it leads to an exception higher in call hierarchy
foo?.bar?.baz
GETTING RID OF NULL
NON-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]
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 IS MANDARORY!
OPTION
CREATING 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 None
val certainAgain = Option("Sun comes up") // Some(Sun comes up)
OPTION
WORKING WITH OPTION AN OLD WAY
Don't do this (only in exceptional cases)
// 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
}
OPTION
PATTERN 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
}
OPTION
PROVIDING A DEFAULT VALUE
Default value is by-name parameter. It's evaluated lazily.
// any long computation for default value
val foo = request.param("foo") getOrElse ("Default foo")
OPTION
TREATING IT FUNCTIONAL WAY
Think of Option as collection
It is biased towards Some
You can map, flatMapor compose Option(s) when it
contains value, i.e. it's Some
OPTION
EXAMPLE
Suppose following model and DAO
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
// 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
// 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
Usage in left side of generator
// Extracting age, take 3
val ageOpt = for {
user <- UserDao.findById(userId)
age <- user.age
} yield age // Returns Option[Int]
// 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
Different notation
Both prints
Rule of thumb: wrap all mandatory fields with Option and
then 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"
OPTION
CHAINING
Use case: Fetching or creating the user
More appropriate, when Useris 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
OPTION
MORE 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
...
}
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 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
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]
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:
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
EITHER IS NOT BIASED
EITHER
CREATING EITHER
There is no Either(...)factory method on companion
object.
def parseAge(input: String): Either[String, Int] = {
try {
Right(input.toInt)
} catch {
case nfe: NumberFormatException => Left("Unable to parse age")
}
}
EITHER
WORKING 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
}
EITHER
PATTERN 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
}
}
EITHER
PROJECTIONS
You cannot directly use instance of Eitheras collection.
It's unbiased, you have to define what is your prefered side.
Working on success, only 1st error is returned.
either.rightreturns RightProjection
def parseFilter(input: String): Either[String, UserFilter] = {
for {
name <- parseName(input).right
age <- parseAge(input).right
} yield Right(UserFilter(name, age))
}
EITHER
PROJECTIONS, TAKE 2
Working on both sides, all errors are collected.
either.leftreturns 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)
}
}
EITHER
PROJECTIONS, TAKE 3
Both projection are biased wrappers for Either
You can use map, flatMapon 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]
EITHER
PROJECTIONS, 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
for {
name <- parseName(input).right
bigName <- Right(name.capitalize).right
} yield bigName
EITHER
FOLDING
Allows transforming the Eitherregardless if it's Rightor
Lefton the same type
Accepts functions, both are evaluated lazily. Result from both
functions has same type.
// 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))
)
EITHER
MORE 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]
}
THROWING AND CATCHING EXCEPTIONS
SOMETIMES THINGS REALLY GO WRONG
You can use classic try/catch/finallyconstruct
def parseAge(input: String): Either[String, Int] = {
try {
Right(input.toInt)
} catch {
case nfe: NumberFormatException => Left("Unable to parse age")
}
}
THROWING AND CATCHING EXCEPTIONS
SOMETIMES THINGS REALLY GO WRONG, TAKE 2
But, it's try/catch/finallyon steroids thanks to pattern
matching
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
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()
}
WHAT'S WRONG WITH EXCEPTIONS
1. 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
SHOULD I THROW AN EXCEPTION?
No, there is better approach
EXCEPTION HANDLING FUNCTIONAL WAY
Please welcome
import scala.util.control._
and
Collection of Throwableor value
sealed trait Try[A]
case class Failure[A](e: Throwable) extends Try[A]
case class Success[A](value: A) extends Try[A]
TRY
1. States that computation may be Success[T]or may be
Failure[T]ending with Throwableon type level
2. Similar to Option, it's Successbiased
3. It's try/catchwithout boilerplate
4. Again it clearly documents what is happening
TRY
LIKE OPTION
All the operations from Optionare present
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
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
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
It returns Catch[T]
catching(classOf[NumberFormatException]) {
input.toInt
} // Returns Catch[Int]
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]
SCALA.UTIL.CONTROL._
SIDE-EFFECTING
ignoring(classOf[NumberFormatException]) {
println(input.toInt)
} // Returns Catch[Unit]
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)
}
SCALA.UTIL.CONTROL._
PROVIDING DEFAULT VALUE
val age = failAsValue(classOf[NumberFormatException])(0) {
input.toInt
}
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
}
SCALA.UTIL.CONTROL._
There's more to cover and explore,
please check out the .Scala documentation
THANKS FOR YOUR ATTENTION

Contenu connexe

Tendances

Java 5 New Feature
Java 5 New FeatureJava 5 New Feature
Java 5 New Featurexcoda
 
Swift Programming Language
Swift Programming LanguageSwift Programming Language
Swift Programming LanguageGiuseppe Arici
 
Acciones para AmigoBot
Acciones para AmigoBotAcciones para AmigoBot
Acciones para AmigoBotjhonsoomelol
 
Thumbtack Expertise Days # 5 - Javaz
Thumbtack Expertise Days # 5 - JavazThumbtack Expertise Days # 5 - Javaz
Thumbtack Expertise Days # 5 - JavazAlexey Remnev
 
Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...Arthur Puthin
 
Type Classes in Scala and Haskell
Type Classes in Scala and HaskellType Classes in Scala and Haskell
Type Classes in Scala and HaskellHermann Hueck
 
Cocoa Design Patterns in Swift
Cocoa Design Patterns in SwiftCocoa Design Patterns in Swift
Cocoa Design Patterns in SwiftMichele Titolo
 
Core csharp and net quick reference
Core csharp and net quick referenceCore csharp and net quick reference
Core csharp and net quick referenceilesh raval
 
Peyton jones-2011-type classes
Peyton jones-2011-type classesPeyton jones-2011-type classes
Peyton jones-2011-type classesTakayuki Muranushi
 
Peyton jones-2009-fun with-type_functions-slide
Peyton jones-2009-fun with-type_functions-slidePeyton jones-2009-fun with-type_functions-slide
Peyton jones-2009-fun with-type_functions-slideTakayuki Muranushi
 
JAVA Tutorial- Do's and Don'ts of Java programming
JAVA Tutorial- Do's and Don'ts of Java programmingJAVA Tutorial- Do's and Don'ts of Java programming
JAVA Tutorial- Do's and Don'ts of Java programmingKeshav Kumar
 
Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)Hermann Hueck
 
2 kotlin vs. java: what java has that kotlin does not
2  kotlin vs. java: what java has that kotlin does not2  kotlin vs. java: what java has that kotlin does not
2 kotlin vs. java: what java has that kotlin does notSergey Bandysik
 
Uses & Abuses of Mocks & Stubs
Uses & Abuses of Mocks & StubsUses & Abuses of Mocks & Stubs
Uses & Abuses of Mocks & StubsPatchSpace Ltd
 
Google collections api an introduction
Google collections api   an introductionGoogle collections api   an introduction
Google collections api an introductiongosain20
 

Tendances (20)

Java 5 New Feature
Java 5 New FeatureJava 5 New Feature
Java 5 New Feature
 
Swift Programming Language
Swift Programming LanguageSwift Programming Language
Swift Programming Language
 
Acciones para AmigoBot
Acciones para AmigoBotAcciones para AmigoBot
Acciones para AmigoBot
 
Thumbtack Expertise Days # 5 - Javaz
Thumbtack Expertise Days # 5 - JavazThumbtack Expertise Days # 5 - Javaz
Thumbtack Expertise Days # 5 - Javaz
 
Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...
 
Type Classes in Scala and Haskell
Type Classes in Scala and HaskellType Classes in Scala and Haskell
Type Classes in Scala and Haskell
 
JAVA OOP
JAVA OOPJAVA OOP
JAVA OOP
 
Cocoa Design Patterns in Swift
Cocoa Design Patterns in SwiftCocoa Design Patterns in Swift
Cocoa Design Patterns in Swift
 
Core csharp and net quick reference
Core csharp and net quick referenceCore csharp and net quick reference
Core csharp and net quick reference
 
Peyton jones-2011-type classes
Peyton jones-2011-type classesPeyton jones-2011-type classes
Peyton jones-2011-type classes
 
Peyton jones-2009-fun with-type_functions-slide
Peyton jones-2009-fun with-type_functions-slidePeyton jones-2009-fun with-type_functions-slide
Peyton jones-2009-fun with-type_functions-slide
 
JAVA Tutorial- Do's and Don'ts of Java programming
JAVA Tutorial- Do's and Don'ts of Java programmingJAVA Tutorial- Do's and Don'ts of Java programming
JAVA Tutorial- Do's and Don'ts of Java programming
 
Scala Paradigms
Scala ParadigmsScala Paradigms
Scala Paradigms
 
Erjang - A JVM-based Erlang VM
Erjang - A JVM-based Erlang VMErjang - A JVM-based Erlang VM
Erjang - A JVM-based Erlang VM
 
Python
PythonPython
Python
 
Cheat Sheet java
Cheat Sheet javaCheat Sheet java
Cheat Sheet java
 
Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)
 
2 kotlin vs. java: what java has that kotlin does not
2  kotlin vs. java: what java has that kotlin does not2  kotlin vs. java: what java has that kotlin does not
2 kotlin vs. java: what java has that kotlin does not
 
Uses & Abuses of Mocks & Stubs
Uses & Abuses of Mocks & StubsUses & Abuses of Mocks & Stubs
Uses & Abuses of Mocks & Stubs
 
Google collections api an introduction
Google collections api   an introductionGoogle collections api   an introduction
Google collections api an introduction
 

Similaire à Option, Either, Try and what to do with corner cases when they arise

Stuff you didn't know about action script
Stuff you didn't know about action scriptStuff you didn't know about action script
Stuff you didn't know about action scriptChristophe Herreman
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2Hang Zhao
 
A Brief Intro to Scala
A Brief Intro to ScalaA Brief Intro to Scala
A Brief Intro to ScalaTim Underwood
 
No excuses, switch to kotlin
No excuses, switch to kotlinNo excuses, switch to kotlin
No excuses, switch to kotlinThijs Suijten
 
Privet Kotlin (Windy City DevFest)
Privet Kotlin (Windy City DevFest)Privet Kotlin (Windy City DevFest)
Privet Kotlin (Windy City DevFest)Cody Engel
 
Bologna Developer Zone - About Kotlin
Bologna Developer Zone - About KotlinBologna Developer Zone - About Kotlin
Bologna Developer Zone - About KotlinMarco Vasapollo
 
Scala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar ProkopecScala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar ProkopecLoïc Descotte
 
Google Guava for cleaner code
Google Guava for cleaner codeGoogle Guava for cleaner code
Google Guava for cleaner codeMite Mitreski
 
Android & Kotlin - The code awakens #03
Android & Kotlin - The code awakens #03Android & Kotlin - The code awakens #03
Android & Kotlin - The code awakens #03Omar Miatello
 
Functional programming ii
Functional programming iiFunctional programming ii
Functional programming iiPrashant Kalkar
 
Java Generics for Dummies
Java Generics for DummiesJava Generics for Dummies
Java Generics for Dummiesknutmork
 

Similaire à Option, Either, Try and what to do with corner cases when they arise (20)

Stuff you didn't know about action script
Stuff you didn't know about action scriptStuff you didn't know about action script
Stuff you didn't know about action script
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2
 
Introduction to Scala
Introduction to ScalaIntroduction to Scala
Introduction to Scala
 
Scala introduction
Scala introductionScala introduction
Scala introduction
 
A Brief Intro to Scala
A Brief Intro to ScalaA Brief Intro to Scala
A Brief Intro to Scala
 
Exploring ES6
Exploring ES6Exploring ES6
Exploring ES6
 
No excuses, switch to kotlin
No excuses, switch to kotlinNo excuses, switch to kotlin
No excuses, switch to kotlin
 
Privet Kotlin (Windy City DevFest)
Privet Kotlin (Windy City DevFest)Privet Kotlin (Windy City DevFest)
Privet Kotlin (Windy City DevFest)
 
ES6 and BEYOND
ES6 and BEYONDES6 and BEYOND
ES6 and BEYOND
 
Bologna Developer Zone - About Kotlin
Bologna Developer Zone - About KotlinBologna Developer Zone - About Kotlin
Bologna Developer Zone - About Kotlin
 
Scala for curious
Scala for curiousScala for curious
Scala for curious
 
DITEC - Programming with Java
DITEC - Programming with JavaDITEC - Programming with Java
DITEC - Programming with Java
 
Kotlin
KotlinKotlin
Kotlin
 
Php & my sql
Php & my sqlPhp & my sql
Php & my sql
 
Scala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar ProkopecScala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar Prokopec
 
Google Guava for cleaner code
Google Guava for cleaner codeGoogle Guava for cleaner code
Google Guava for cleaner code
 
Android & Kotlin - The code awakens #03
Android & Kotlin - The code awakens #03Android & Kotlin - The code awakens #03
Android & Kotlin - The code awakens #03
 
Functional programming ii
Functional programming iiFunctional programming ii
Functional programming ii
 
Java Generics for Dummies
Java Generics for DummiesJava Generics for Dummies
Java Generics for Dummies
 
Txjs
TxjsTxjs
Txjs
 

Plus de Michal Bigos

All About ... Functions
All About ... FunctionsAll About ... Functions
All About ... FunctionsMichal Bigos
 
Scala eXchange 2013 Report
Scala eXchange 2013 ReportScala eXchange 2013 Report
Scala eXchange 2013 ReportMichal Bigos
 
Functional Domain Modeling
Functional Domain ModelingFunctional Domain Modeling
Functional Domain ModelingMichal Bigos
 
Dependency injection in scala
Dependency injection in scalaDependency injection in scala
Dependency injection in scalaMichal Bigos
 
Integration Testing With ScalaTest and MongoDB
Integration Testing With ScalaTest and MongoDBIntegration Testing With ScalaTest and MongoDB
Integration Testing With ScalaTest and MongoDBMichal Bigos
 

Plus de Michal Bigos (6)

All About ... Functions
All About ... FunctionsAll About ... Functions
All About ... Functions
 
Scala eXchange 2013 Report
Scala eXchange 2013 ReportScala eXchange 2013 Report
Scala eXchange 2013 Report
 
Functional Domain Modeling
Functional Domain ModelingFunctional Domain Modeling
Functional Domain Modeling
 
SBT Crash Course
SBT Crash CourseSBT Crash Course
SBT Crash Course
 
Dependency injection in scala
Dependency injection in scalaDependency injection in scala
Dependency injection in scala
 
Integration Testing With ScalaTest and MongoDB
Integration Testing With ScalaTest and MongoDBIntegration Testing With ScalaTest and MongoDB
Integration Testing With ScalaTest and MongoDB
 

Dernier

REMIFENTANIL: An Ultra short acting opioid.pptx
REMIFENTANIL: An Ultra short acting opioid.pptxREMIFENTANIL: An Ultra short acting opioid.pptx
REMIFENTANIL: An Ultra short acting opioid.pptxDr. Ravikiran H M Gowda
 
How to Create and Manage Wizard in Odoo 17
How to Create and Manage Wizard in Odoo 17How to Create and Manage Wizard in Odoo 17
How to Create and Manage Wizard in Odoo 17Celine George
 
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...Pooja Bhuva
 
Philosophy of china and it's charactistics
Philosophy of china and it's charactisticsPhilosophy of china and it's charactistics
Philosophy of china and it's charactisticshameyhk98
 
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptxHMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptxEsquimalt MFRC
 
21st_Century_Skills_Framework_Final_Presentation_2.pptx
21st_Century_Skills_Framework_Final_Presentation_2.pptx21st_Century_Skills_Framework_Final_Presentation_2.pptx
21st_Century_Skills_Framework_Final_Presentation_2.pptxJoelynRubio1
 
On_Translating_a_Tamil_Poem_by_A_K_Ramanujan.pptx
On_Translating_a_Tamil_Poem_by_A_K_Ramanujan.pptxOn_Translating_a_Tamil_Poem_by_A_K_Ramanujan.pptx
On_Translating_a_Tamil_Poem_by_A_K_Ramanujan.pptxPooja Bhuva
 
OSCM Unit 2_Operations Processes & Systems
OSCM Unit 2_Operations Processes & SystemsOSCM Unit 2_Operations Processes & Systems
OSCM Unit 2_Operations Processes & SystemsSandeep D Chaudhary
 
Jamworks pilot and AI at Jisc (20/03/2024)
Jamworks pilot and AI at Jisc (20/03/2024)Jamworks pilot and AI at Jisc (20/03/2024)
Jamworks pilot and AI at Jisc (20/03/2024)Jisc
 
Jual Obat Aborsi Hongkong ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...
Jual Obat Aborsi Hongkong ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...Jual Obat Aborsi Hongkong ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...
Jual Obat Aborsi Hongkong ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...ZurliaSoop
 
Exploring_the_Narrative_Style_of_Amitav_Ghoshs_Gun_Island.pptx
Exploring_the_Narrative_Style_of_Amitav_Ghoshs_Gun_Island.pptxExploring_the_Narrative_Style_of_Amitav_Ghoshs_Gun_Island.pptx
Exploring_the_Narrative_Style_of_Amitav_Ghoshs_Gun_Island.pptxPooja Bhuva
 
HMCS Vancouver Pre-Deployment Brief - May 2024 (Web Version).pptx
HMCS Vancouver Pre-Deployment Brief - May 2024 (Web Version).pptxHMCS Vancouver Pre-Deployment Brief - May 2024 (Web Version).pptx
HMCS Vancouver Pre-Deployment Brief - May 2024 (Web Version).pptxmarlenawright1
 
Wellbeing inclusion and digital dystopias.pptx
Wellbeing inclusion and digital dystopias.pptxWellbeing inclusion and digital dystopias.pptx
Wellbeing inclusion and digital dystopias.pptxJisc
 
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...Nguyen Thanh Tu Collection
 
Kodo Millet PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
Kodo Millet  PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...Kodo Millet  PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
Kodo Millet PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...pradhanghanshyam7136
 
Understanding Accommodations and Modifications
Understanding  Accommodations and ModificationsUnderstanding  Accommodations and Modifications
Understanding Accommodations and ModificationsMJDuyan
 
Graduate Outcomes Presentation Slides - English
Graduate Outcomes Presentation Slides - EnglishGraduate Outcomes Presentation Slides - English
Graduate Outcomes Presentation Slides - Englishneillewis46
 
latest AZ-104 Exam Questions and Answers
latest AZ-104 Exam Questions and Answerslatest AZ-104 Exam Questions and Answers
latest AZ-104 Exam Questions and Answersdalebeck957
 
NO1 Top Black Magic Specialist In Lahore Black magic In Pakistan Kala Ilam Ex...
NO1 Top Black Magic Specialist In Lahore Black magic In Pakistan Kala Ilam Ex...NO1 Top Black Magic Specialist In Lahore Black magic In Pakistan Kala Ilam Ex...
NO1 Top Black Magic Specialist In Lahore Black magic In Pakistan Kala Ilam Ex...Amil baba
 

Dernier (20)

REMIFENTANIL: An Ultra short acting opioid.pptx
REMIFENTANIL: An Ultra short acting opioid.pptxREMIFENTANIL: An Ultra short acting opioid.pptx
REMIFENTANIL: An Ultra short acting opioid.pptx
 
How to Create and Manage Wizard in Odoo 17
How to Create and Manage Wizard in Odoo 17How to Create and Manage Wizard in Odoo 17
How to Create and Manage Wizard in Odoo 17
 
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
 
Philosophy of china and it's charactistics
Philosophy of china and it's charactisticsPhilosophy of china and it's charactistics
Philosophy of china and it's charactistics
 
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptxHMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
 
21st_Century_Skills_Framework_Final_Presentation_2.pptx
21st_Century_Skills_Framework_Final_Presentation_2.pptx21st_Century_Skills_Framework_Final_Presentation_2.pptx
21st_Century_Skills_Framework_Final_Presentation_2.pptx
 
On_Translating_a_Tamil_Poem_by_A_K_Ramanujan.pptx
On_Translating_a_Tamil_Poem_by_A_K_Ramanujan.pptxOn_Translating_a_Tamil_Poem_by_A_K_Ramanujan.pptx
On_Translating_a_Tamil_Poem_by_A_K_Ramanujan.pptx
 
OSCM Unit 2_Operations Processes & Systems
OSCM Unit 2_Operations Processes & SystemsOSCM Unit 2_Operations Processes & Systems
OSCM Unit 2_Operations Processes & Systems
 
Jamworks pilot and AI at Jisc (20/03/2024)
Jamworks pilot and AI at Jisc (20/03/2024)Jamworks pilot and AI at Jisc (20/03/2024)
Jamworks pilot and AI at Jisc (20/03/2024)
 
Jual Obat Aborsi Hongkong ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...
Jual Obat Aborsi Hongkong ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...Jual Obat Aborsi Hongkong ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...
Jual Obat Aborsi Hongkong ( Asli No.1 ) 085657271886 Obat Penggugur Kandungan...
 
Exploring_the_Narrative_Style_of_Amitav_Ghoshs_Gun_Island.pptx
Exploring_the_Narrative_Style_of_Amitav_Ghoshs_Gun_Island.pptxExploring_the_Narrative_Style_of_Amitav_Ghoshs_Gun_Island.pptx
Exploring_the_Narrative_Style_of_Amitav_Ghoshs_Gun_Island.pptx
 
HMCS Vancouver Pre-Deployment Brief - May 2024 (Web Version).pptx
HMCS Vancouver Pre-Deployment Brief - May 2024 (Web Version).pptxHMCS Vancouver Pre-Deployment Brief - May 2024 (Web Version).pptx
HMCS Vancouver Pre-Deployment Brief - May 2024 (Web Version).pptx
 
Wellbeing inclusion and digital dystopias.pptx
Wellbeing inclusion and digital dystopias.pptxWellbeing inclusion and digital dystopias.pptx
Wellbeing inclusion and digital dystopias.pptx
 
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
 
Kodo Millet PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
Kodo Millet  PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...Kodo Millet  PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
Kodo Millet PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
 
Understanding Accommodations and Modifications
Understanding  Accommodations and ModificationsUnderstanding  Accommodations and Modifications
Understanding Accommodations and Modifications
 
Graduate Outcomes Presentation Slides - English
Graduate Outcomes Presentation Slides - EnglishGraduate Outcomes Presentation Slides - English
Graduate Outcomes Presentation Slides - English
 
latest AZ-104 Exam Questions and Answers
latest AZ-104 Exam Questions and Answerslatest AZ-104 Exam Questions and Answers
latest AZ-104 Exam Questions and Answers
 
NO1 Top Black Magic Specialist In Lahore Black magic In Pakistan Kala Ilam Ex...
NO1 Top Black Magic Specialist In Lahore Black magic In Pakistan Kala Ilam Ex...NO1 Top Black Magic Specialist In Lahore Black magic In Pakistan Kala Ilam Ex...
NO1 Top Black Magic Specialist In Lahore Black magic In Pakistan Kala Ilam Ex...
 
Mehran University Newsletter Vol-X, Issue-I, 2024
Mehran University Newsletter Vol-X, Issue-I, 2024Mehran University Newsletter Vol-X, Issue-I, 2024
Mehran University Newsletter Vol-X, Issue-I, 2024
 

Option, Either, Try and what to do with corner cases when they arise

  • 1. OPTION, EITHER, TRY AND WHAT TO DO WITH CORNER CASES WHEN THEY ARISE KNOW YOUR LIBRARY MINI-SERIES By /Michal Bigos @teliatko
  • 2. 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
  • 3. WHY NULL ISN'T AN OPTION CONSIDER 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") }
  • 4. WHY NULL ISN'T AN OPTION WHAT'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") }
  • 5. DEALING WITH NON-EXISTENCE DIFFERENT APPROACHES COMPARED Java relies on sad null Groovy provides null-safe operator for accessing properties Clojure uses nilwhich is okay very often, but sometimes it leads to an exception higher in call hierarchy foo?.bar?.baz
  • 6. GETTING RID OF NULL NON-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]
  • 7. 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
  • 9. OPTION CREATING 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 None val certainAgain = Option("Sun comes up") // Some(Sun comes up)
  • 10. OPTION WORKING WITH OPTION AN OLD WAY Don't do this (only in exceptional cases) // 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 }
  • 11. OPTION PATTERN 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 }
  • 12. OPTION PROVIDING A DEFAULT VALUE Default value is by-name parameter. It's evaluated lazily. // any long computation for default value val foo = request.param("foo") getOrElse ("Default foo")
  • 13. OPTION TREATING IT FUNCTIONAL WAY Think of Option as collection It is biased towards Some You can map, flatMapor compose Option(s) when it contains value, i.e. it's Some
  • 14. OPTION EXAMPLE Suppose following model and DAO 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 }
  • 15. OPTION SIDE-EFFECTING Use case: Printing the user name // 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 )
  • 16. OPTION MAP, FLATMAP & CO. Use case: Extracting age // 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]
  • 17. OPTION FOR COMPREHENSIONS Same use case as before Usage in left side of generator // Extracting age, take 3 val ageOpt = for { user <- UserDao.findById(userId) age <- user.age } yield age // Returns Option[Int] // Extracting age, take 3 val ageOpt = for { User(_, Some(age)) <- UserDao.findById(userId) } yield age // Returns Option[Int]
  • 18. OPTION COMPOSING TO LIST Use case: Pretty-print of user Different notation Both prints Rule of thumb: wrap all mandatory fields with Option and then 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"
  • 19.
  • 20. OPTION CHAINING Use case: Fetching or creating the user More appropriate, when Useris 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
  • 21. OPTION MORE 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 ... }
  • 22. 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 FilterException def parseFilter(input: String): Option[UserFilter] throws FilterException { ... } // caller side val filter = try { parseFilter(input) } catch { case e: FilterException => whatToDoInTheMiddleOfTheCode(e) }
  • 23. Exception doesn't help much. It only introduces overhead
  • 24. 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]
  • 25. 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: 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
  • 26. EITHER IS NOT BIASED
  • 27. EITHER CREATING EITHER There is no Either(...)factory method on companion object. def parseAge(input: String): Either[String, Int] = { try { Right(input.toInt) } catch { case nfe: NumberFormatException => Left("Unable to parse age") } }
  • 28. EITHER WORKING 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 }
  • 29. EITHER PATTERN 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 } }
  • 30. EITHER PROJECTIONS You cannot directly use instance of Eitheras collection. It's unbiased, you have to define what is your prefered side. Working on success, only 1st error is returned. either.rightreturns RightProjection def parseFilter(input: String): Either[String, UserFilter] = { for { name <- parseName(input).right age <- parseAge(input).right } yield Right(UserFilter(name, age)) }
  • 31. EITHER PROJECTIONS, TAKE 2 Working on both sides, all errors are collected. either.leftreturns 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) } }
  • 32. EITHER PROJECTIONS, TAKE 3 Both projection are biased wrappers for Either You can use map, flatMapon 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]
  • 33. EITHER PROJECTIONS, 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
  • 34. for { name <- parseName(input).right bigName <- Right(name.capitalize).right } yield bigName
  • 35. EITHER FOLDING Allows transforming the Eitherregardless if it's Rightor Lefton the same type Accepts functions, both are evaluated lazily. Result from both functions has same type. // 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)) )
  • 36. EITHER MORE 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] }
  • 37. THROWING AND CATCHING EXCEPTIONS SOMETIMES THINGS REALLY GO WRONG You can use classic try/catch/finallyconstruct def parseAge(input: String): Either[String, Int] = { try { Right(input.toInt) } catch { case nfe: NumberFormatException => Left("Unable to parse age") } }
  • 38. THROWING AND CATCHING EXCEPTIONS SOMETIMES THINGS REALLY GO WRONG, TAKE 2 But, it's try/catch/finallyon steroids thanks to pattern matching 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() }
  • 39. THROWING AND CATCHING EXCEPTIONS SOMETIMES 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() }
  • 40.
  • 41. WHAT'S WRONG WITH EXCEPTIONS 1. 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
  • 42. SHOULD I THROW AN EXCEPTION? No, there is better approach
  • 43. EXCEPTION HANDLING FUNCTIONAL WAY Please welcome import scala.util.control._ and Collection of Throwableor value sealed trait Try[A] case class Failure[A](e: Throwable) extends Try[A] case class Success[A](value: A) extends Try[A]
  • 44. TRY 1. States that computation may be Success[T]or may be Failure[T]ending with Throwableon type level 2. Similar to Option, it's Successbiased 3. It's try/catchwithout boilerplate 4. Again it clearly documents what is happening
  • 45. TRY LIKE OPTION All the operations from Optionare present 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] }
  • 46. TRY BUT 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
  • 47. SCALA.UTIL.CONTROL._ 1. Utility methods for common exception handling patterns 2. Less boiler plate than try/catch/finally
  • 48. SCALA.UTIL.CONTROL._ CATCHING AN EXCEPTION It returns Catch[T] catching(classOf[NumberFormatException]) { input.toInt } // Returns Catch[Int]
  • 49. 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]
  • 50.
  • 52. 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) }
  • 53. SCALA.UTIL.CONTROL._ PROVIDING DEFAULT VALUE val age = failAsValue(classOf[NumberFormatException])(0) { input.toInt }
  • 54. 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 }
  • 55. SCALA.UTIL.CONTROL._ There's more to cover and explore, please check out the .Scala documentation
  • 56. THANKS FOR YOUR ATTENTION