user13392352
user13392352

Reputation: 194

How to check errors cleanly in scala functionally?

Is there a way to avoid the nesting in the code below when checking for errors without simply chaining functions?

val a: Either[Value, Exception]
val b: Either[Value, Exception]
val c: Either[Value, Exception]

def combine(a: Either[Value, Exception], 
            b: Either[Value, Exception], 
            c: Either[Value, Exception]) = 
{
  a match {
    case Right(a) => Right(a)
    case Left(a)  => b match {
                       case Right(b) => Right(b)
                       case Left(b)  => c match {
                                          case Right(c) => Right(c)
                                          case Left(c)  => a + b + c
                                        }
                     }
   }
}

Upvotes: 0

Views: 119

Answers (2)

Mario Galic
Mario Galic

Reputation: 48420

Since a, b, and c are independent of each other Applicative approach can also avoid "the pyramid of doom"

import cats.implicits._
(a, b, c).mapN(_ + _ + _)

of if you wish to keep the happy value on the left side, consider swapping the sides like so

import cats.implicits._
(a.swap, b.swap, c.swap).mapN(_ + _ + _).swap

Upvotes: 2

Mateusz Kubuszok
Mateusz Kubuszok

Reputation: 27535

First: by convention (imported from Haskell, where it made more sense) errors are Left and correct values are Right.

Once we establish that convention: .map allows mapping Right value (it leaves Left value intact) while .flatMap allows you to turn Right value into either Left or Right - on Right it will continue composition, while Left will break the circuit.

Having:

val a: Either[Exception, Value]
val b: Either[Exception, Value]
val c: Either[Exception, Value]

you could write:

val abc: Either[Exception, Value] =
  a.flatMap { aValue =>
    b.flatMap { bValue =>
      c.map { cValue =>
        (aValue, bValue, cValue)
      }
    }
  }

which thanks to for comprehension can be written shorter as:

val abc = for {
  aValue <- a
  bValue <- b
  cValue <- c
} yield (aValue, bValue, cValue)

However, if you use java.lang.Exception as you error, then it would make more sense to use Try because Try[A] is very similar to Either[Throwable, A], except that it also can catch exceptions thrown inside of it (and is shorter to write).

Upvotes: 5

Related Questions