Skip to content

Instantly share code, notes, and snippets.

@eboto
Created August 8, 2012 22:32
Show Gist options
  • Save eboto/3299446 to your computer and use it in GitHub Desktop.
Save eboto/3299446 to your computer and use it in GitHub Desktop.
How to use Scala's Either[A, B] sanely for error handling
//
// Demonstration on how to use Scala's Either[A, B] type to include error cases in function return values
// and eventually transform those domain errors into more useful end types, like Redirects.
//
// This demonstration uses the example of ordering a product, where ordering can fail for a variety
// of reasons including failure in our payment provider (Stripe) or failure due to insufficient
// product inventory.
//
object Main {
def main(args: Array[String]) {
// Attempt a purchase, which returns an Either[PurchaseFailed, Order]
val errorOrOrder = performPurchase
// Fold errorOrOrder, which could have either been a failure or a success, into a single
// type. Here we fold it into a String, but in the case of a Play app, we would fold it
// into a Redirect type.
//
// fold takes two parameters:
// (1) a function that takes the error type and returns something,
// (2) a function that takes the success type and returns something.
//
// fold returns the closest common ancestor type between the return value
// of the first and second argument functions.
//
// In our case the error type is PurchaseFailed, the success type is Order.
val result = errorOrOrder.fold(
(error) => error match {
case stripeError: PurchaseFailedStripeError =>
"A redirect to the stripe error page"
case insufficientInventory: PurchaseFailedInsufficientInventory =>
"A redirect to the insufficient inventory page"
},
(successfulOrder) =>
"A redirect to the order confirmation page"
)
println("Result was: " + result)
println("")
}
// The function that actually performs the purchase
def performPurchase: Either[PurchaseFailed, Order] = {
// Create fake Celebrity Products that we're buying
val celeb = new Celebrity
val product = new Product
// Fake code that models the different error or success cases
val oopsStripeError = false
val oopsInsufficientInventory = true
if (oopsStripeError) {
Left(PurchaseFailedStripeError(celeb, product))
}
else if (oopsInsufficientInventory) {
Left(PurchaseFailedInsufficientInventory(celeb, product))
}
else {
Right(new Order(1L))
}
}
}
// Failure base type
sealed trait PurchaseFailed
// Our two failure cases for if the payment vendor failed or there was insufficient inventory
case class PurchaseFailedStripeError(celebrity: Celebrity, product: Product) extends PurchaseFailed
case class PurchaseFailedInsufficientInventory(celebrity: Celebrity, product: Product) extends PurchaseFailed
// Faux domain model objects
class Celebrity
class Product
class Order(id: Long)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment