Friday, November 20, 2009

Either

The Scala Either class is similar in function as Option but it represents a choice. For example a method can return Either an Int or an Exception. Usage example:
  1. // If string can be converted to an Int then return Right[Int] otherwise
  2. // return a Left[String] with the error message
  3. // by convention the error state will always be the Left
  4. scala> def toInt(string:String):Either[String,Int] = {
  5.      | try { Right(string.toInt) }
  6.      | catch { case e => Left("Error: "+e.getMessage) }
  7.      | }
  8. toInt: (string: String)Either[String,Int]
  9. scala> toInt("1")
  10. res0: Either[String,Int] = Right(1)
  11. scala> toInt("booger")
  12. res1: Either[String,Int] = Left(Error: For input string: "booger")

By convention if one return value is a error state and the other the expected state then Right will contain the expected state and Left will contain the error state.
  1. scala> def toInt(string:String):Either[String,Int] = {
  2.      | try { Right(string.toInt) }
  3.      | catch { case e => Left("Error: "+e.getMessage) }
  4.      | }
  5. toInt: (string: String)Either[String,Int]
  6. scala> toInt("1")
  7. res0: Either[String,Int] = Right(1)
  8. scala> toInt("booger")
  9. res1: Either[String,Int] = Left(Error: For input string: "booger")
  10. // you can test if the value is a left or right value quite easily
  11. scala> res0.isLeft
  12. res2: Boolean = false
  13. scala> res1.isRight
  14. res3: Boolean = false
  15. scala> res0.isRight
  16. res4: Boolean = true
  17. scala> res1.isLeft 
  18. res5: Boolean = true
  19. // or matching can be used
  20. scala> res0 match {                                   
  21.      | case Left(l) => println("it is a left")        
  22.      | case Right(r) => println("it is a right")      
  23.      | }
  24. it is a right
  25. scala> res1 match {                             
  26.      | case Left(l) => println("it is a left")  
  27.      | case Right(r) => println("it is a right")
  28.      | }
  29. it is a left
  30. // Perhaps cleaner than matching even is being able to pass 
  31. // functions to the fold method:
  32. // define one function for each side of the either
  33. // first the function to handle the left side case
  34. scala> def leftFunc(errMsg:String) = println("there has been an error")             
  35. leftFunc: (errMsg: String)Unit
  36. // next the function the handle the right side case
  37. scala> def rightFunc(i:Int) = println(i+" was calculated")              
  38. rightFunc: (i: Int)Unit
  39. // then you can pass the two functions to the fold method
  40. // and the correct one will be invoked
  41. scala> res0 fold (leftFunc _, rightFunc _)
  42. 1 was calculated
  43. scala> res1 fold (leftFunc _, rightFunc _)
  44. there has been an error

1 comment:

  1. It's interesting to notice that:

    instance (Error e) => Monad (Either e)

    that means that it would be possible to chain
    operations on Either using for comprehension as
    we do with Option while getting Error message handling.

    The implementation is left to the reader :-)

    ReplyDelete