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