On Github guesswho311 / ia-scala-options-either
Jonathan Quist - jonathan.quist@banno.com iascala - March 2014
If you have programmed in the past, chances are you've seen this guy before...
A Null Pointer Exception occurs when you attempt to perform some operation on a null value. In scala there is also a NoSuchElementException which occurs if you try to access data that isn't there.
class Apples case object RedApple extends Apples case object GreenApple extends Apples def removeGreenApples(basketOfApples:List[Apples]): List[Apples] = basketOfApples.filterNot(apple => apple == GreenApple) def eatAnApple(basketOfApples:List[Apples]) = println(basketOfApples.head) val basketOfApples = List(GreenApple, GreenApple, RedApple, GreenApple, GreenApple) eatAnApple(removeGreenApples(basketOfApples))
RedAppleWhat happens when our basketOfApples has no red apples in it?
case class UserAccount(userId: Int, lastName: String, firstName: String, age: Int) val users:List[UserAccount] = List(UserAccount(1, "Doe", "John", 18), UserAccount(2, "Johnson", "Steve", 22) //How can we grab a specific user from this list? //What if the user we're looking for doesnt exist?
Option[A] is simply a container for the type A. If the value of type A exists then Option[A] would be an instance of Some[A]. Otherwise is there is no value then Option[A] would be None. Lets look at some code!
val hello: Option[String] = Some("Hello World!") val pleaseNoHelloWorld: Option[String] = None
case class UserAccount(userId: Int, lastName: String, firstName: String, age: Option[Int]) def getUserById(users: List[UserAccount], userId: Int): Option[UserAccount] = { users.find(user => user.userId == userId) } val users:List[UserAccount] = List(UserAccount(1, "Doe", "John", Some(32)), UserAccount(2, "Johnson", "Steve", Some(30)), UserAccount(3, "Jon", "Doey", None)) val user1 = getUserById(users, 1) println(user1)which will print out
Some(UserAccount(1,Doe,John,Some(32)))
None //if we change the call to getUserById(users, 4)
def displayAge(user: UserAccount) = user.age match { case Some(e) => println(e) case None => println("No age provided") } val user1 = UserAccount(1, "Doe", "John", Some(35)) val user2 = UserAccount(2, "Smith", "Zach", None) displayAge(user1) displayAge(user2)
35 NoneThats cool, but pattern matching kind of seems like overkill...
println(user1.age.getOrElse("No age provided"))
val user1 = Some(UserAccount(1, "Doe", "John", Some(35))) println(user1.map(_.age))
Some(Some(35))-- Our Results --
val user1 = Some(UserAccount(1, "Doe", "John", Some(35))) println(user1.flatMap(_.age))
Some(35)
val users:List[UserAccount] = List(UserAccount(1, "Doe", "John", Some(32)), UserAccount(2, "Johnson", "Steve", Some(30)), UserAccount(3, "Smith", "Tori", None)) println(getUserById(users,1).filter(_.age.isDefined)) println(getUserById(users,2).filter(_.age.isDefined))-- Our Results --
Some(UserAccount(1,Doe,John,Some(32))) None
val user1 = Some(UserAccount(1, "Doe", "John", Some(35))) user1.foreach(e => println(e.age))-- Our Results --
Some(35)
object AppleSelector { class Apples case object RedApple extends Apples case object GreenApple extends Apples def removeGreenApples(basketOfApples:List[Apples]): List[Apples] = basketOfApples.filterNot(apple => apple == GreenApple) def selectAnApple(basketOfApples:List[Apples]):Option[Apples] = basketOfApples.headOption def eatAnApple(apple: Option[Apples]) = apple.fold(println("There are no edible apples"))(println(_)) val basketOfApples = List(GreenApple, GreenApple, GreenApple, GreenApple, GreenApple, GreenApple) val basketOfEdibleApples = removeGreenApples(basketOfApples) val appleToEat = selectAnApple(basketOfEdibleApples) eatAnApple(appleToEat) }
When you get an unexpected data type You Either[Handle It, Cry yourself to sleep]
Eithers hold one of two possible results
Left[+A] Right[+B]sealed trait ActionsAgainstUser case object MuteUser extends ActionsAgainstUser def preparePost(message: String):Either[String, ActionsAgainstUser] = { if (message.contains("damn")) Right(WarnUser) else Left(message) }
val postAction = preparePost("How are you doing?") postAction match { case Left(msg) => println(msg) case Right(MuteUser) => muteUser }
Originally Either was used for error handling. An Either used for error handling would look like this: Either[Throwable, Result].
However since scala 2.10 try/catch is now recommended for handling errors.
object FreedomOfSpeechCensor extends App { def warnUser() = println("This is your warning") def muteUser() = println("No more speaking out!") def banUser() = println("Ban Hammer!") def imprisonUser() = println("Go Directly to Jail!") sealed trait ActionsAgainstUser case object WarnUser extends ActionsAgainstUser case object MuteUser extends ActionsAgainstUser case object BanUser extends ActionsAgainstUser case object ImprisonUser extends ActionsAgainstUser def determinePunishment(crime: ActionsAgainstUser) = crime match { case WarnUser => warnUser case MuteUser => muteUser case BanUser => banUser case ImprisonUser => imprisonUser } def checkMessage(message: String):Either[String, ActionsAgainstUser] = message match { case msg if msg.contains("damn") => Right(WarnUser) case msg if msg.contains("my opinion") => Right(MuteUser) case msg if msg.contains("I disagree with you") => Right(BanUser) case msg if msg.contains("Han shot second") => Right(ImprisonUser) case msg => Left(msg) } val message = checkMessage("damn") message.fold(println(msg), determinePunishment(_)) }
wes.iliff@banno.com zach.cox@banno.com We train Scala developers too! *We're now ProfitStars