Last active
August 29, 2015 14:15
-
-
Save colindean/986fde9623140957f0d4 to your computer and use it in GitHub Desktop.
Comparison of Either vs Try in Scala for Angle - inspired by http://underscore.io/blog/posts/2015/02/13/error-handling-without-throwing-your-hands-up.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sealed trait Angle { val degrees: Int } | |
private final case object Perpendicular extends Angle { val degrees = 90 } | |
private final case object Straight extends Angle { val degrees = 180 } | |
private final case class Acute(degrees: Int) extends Angle | |
private final case class Obtuse(degrees: Int) extends Angle | |
private final case class Reflex(degrees: Int) extends Angle | |
object Angle { | |
def apply(degrees: Int): Either[String,Angle] = degrees match { | |
case _ if degrees == 90 ⇒ | |
Right(Perpendicular) | |
case _ if degrees == 180 ⇒ | |
Right(Straight) | |
case _ if degrees >= 0 && degrees < 90 ⇒ | |
Right(Acute(degrees: Int)) | |
case _ if degrees > 90 && degrees < 180 ⇒ | |
Right(Obtuse(degrees: Int)) | |
case _ if degrees > 180 && degrees < 360 ⇒ | |
Right(Reflex(degrees: Int)) | |
case _ ⇒ | |
Left(s"Invalid angle $degrees. Needs to be between 0 and 360.") | |
} | |
} | |
val list = List(Angle(180), Angle(90), Angle(37), Angle(137), Angle(270), Angle(-1)) | |
list.foreach { | |
case Left(angle) => println(s"Yay $angle") | |
case Right(error) => println(s"Boo $error") | |
} | |
def success(angle: Angle) = println(s"Yay $angle") | |
def failure(message: String) = println(s"Boo $message") | |
list.foreach(_.fold(failure, success)) | |
// What I don't like about this is having to remember parameter order, | |
// or relying on an IDE to remind me. | |
// I do like both cases being Functions. They read better, IMO. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.util.{Failure, Try, Success} | |
sealed trait Angle { val degrees: Int } | |
private final case object Perpendicular extends Angle { val degrees = 90 } | |
private final case object Straight extends Angle { val degrees = 180 } | |
private final case class Acute(degrees: Int) extends Angle | |
private final case class Obtuse(degrees: Int) extends Angle | |
private final case class Reflex(degrees: Int) extends Angle | |
object Angle { | |
def apply(degrees: Int): Try[Angle] = degrees match { | |
case _ if degrees == 90 ⇒ | |
Success(Perpendicular) | |
case _ if degrees == 180 ⇒ | |
Success(Straight) | |
case _ if degrees >= 0 && degrees < 90 ⇒ | |
Success(Acute(degrees: Int)) | |
case _ if degrees > 90 && degrees < 180 ⇒ | |
Success(Obtuse(degrees: Int)) | |
case _ if degrees > 180 && degrees < 360 ⇒ | |
Success(Reflex(degrees: Int)) | |
case _ ⇒ | |
Failure(new IllegalArgumentException("Invalid angle $degrees. Needs to be between 0 and 360.")) | |
} | |
} | |
val list = List(Angle(180), Angle(90), Angle(37), Angle(137), Angle(270), Angle(-1)) | |
list.foreach { | |
case Success(angle) => println(s"Yay $angle") | |
case Failure(error) => println(s"Boo $error") | |
} | |
def success(angle: Angle) = println(s"Yay $angle") | |
def failure(message: String) = println(s"Boo $message") | |
list.foreach(_.map(success).recover {case e: IllegalArgumentException => failure(e.getMessage)}) | |
//which would more likely be written as | |
def success(angle: Angle) = println(s"Yay $angle") | |
val failure: PartialFunction[Throwable, Unit] = {case e: IllegalArgumentException => println(e.getMessage)} | |
list.foreach(_.map(success).recover(failure)) | |
// In both cases, the IllegalArgumentException could just be Exception, | |
// since we know that any Failure would wrap an Exception. | |
// By using the partial function, we gain the ability to triage multiple error conditions. | |
// It's unfortunately that this example only has one! | |
// The real usage ends up having better semantics, IMO. | |
// It is better to pass two parameters into one method, or two parameters into one specific method for each? | |
// I like the wrapping of the exception more than having to juggle two different types. | |
// It provides the *option* of throwing up our hands OR handling the error in a semantically sound, type-safe way. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I meant "mostly evil", not "most evil"!
Of course, at some level, exceptions are necessary and good (very outer edges of communicating with the outside world). The reason Rust gets away without having exceptions is because of isolation to threads. Go, Erlang operate the same way. Don't know what the heck is going on with Swift.