Defining these utility functions
implicit def eitherOps[E, A](v: Either[E, A]) = new { def map[B](f: A => B) = v match { case Left(e) => Left(e) case Right(a) => Right(f(a)) } def flatMap[B](f: A => Either[E, B]) = v match { case Left(e) => Left(e) case Right(a) => f(a) } def or(a: A) = v match { case Left(_) => Right(a) case x => x } } def secure[A, B](f: A => B) = new { def run(a: A): Either[Trowable, B] = try { Right(f(a)) } catch { case e => Left(e) } }
and simplifying your
def fA(s : String) = 7 def fB(i : Int) = 1.0 def fC(d : Double) = true
We'll have:
def test(s: String): Either[Throwable, Double] = for { a <- secure(fA).run(s).or(0) b <- secure(fB).run(a).or(0.0) c <- secure(fC).run(b).or(false) } yield result(a, b, c)
Edit
Here is the executable, but unfortunately a more detailed piece of code
object Example { trait EitherOps[E, A] { def self: Either[E, A] def map[B](f: A => B) = self match { case Left(e) => Left(e) case Right(a) => Right(f(a)) } def flatMap[B](f: A => Either[E, B]) = self match { case Left(e) => Left(e) case Right(a) => f(a) } def or(a: A) = self match { case Left(_) => Right(a) case x => x } } trait SecuredFunction[A, B] { def self: A => B def secured(a: A): Either[Throwable, B] = try { Right(self(a)) } catch { case e => Left(e) } } implicit def eitherOps[E, A](v: Either[E, A]) = new EitherOps[E, A] { def self = v } implicit def opsToEither[E, A](v: EitherOps[E, A]) = v.self implicit def secure[A, B](f: A => B) = new SecuredFunction[A, B]{ def self = f } def fA(s : String) = 7 def fB(i : Int) = 1.0 def fC(d : Double) = true def result(i : Int, d : Double, b : Boolean) = { if (b) d else i } def test(s: String): Either[Throwable, Double] = for { a <- (fA _).secured(s) or 0 b <- (fB _).secured(a) or 0.0 c <- (fC _).secured(b) or false } yield result(a, b, c) }
source share