Combining Futures (Twitter) and Either in Scala

爱⌒轻易说出口 提交于 2019-12-05 15:16:07

So now I've tried to use Scalaz Either (which is a right biased Either compared to the neutral scala Either) and the Monad Transformer EitherT and it seems it does exactly what I want. Thanks to Huw and especially Lars Hupel for hinting me in the right direction.

Here is working a sample for Twitter futures and Scalaz Either and EitherT:

import com.twitter.util.{Await, Future}
import scalaz.{Monad, Functor, EitherT, \/}
import scalaz.syntax.ToIdOps

object EitherTest extends App with ToIdOps{

  // make Twitter futures work with EitherT
  implicit val FutureFunctor = new Functor[Future] {
    def map[A, B](a: Future[A])(f: A => B): Future[B] = a map f
  }
  implicit val FutureMonad = new Monad[Future] {
    def point[A](a: => A): Future[A] = Future(a)
    def bind[A, B](fa: Future[A])(f: (A) => Future[B]): Future[B] = fa flatMap f
  }

  // The example begins here:

  case class InvalidInfo(error: String)
  case class Response(msg: String)


  class ComponentA {
    def foo(fail: Boolean): Future[\/[InvalidInfo, Response]] = {
      if(fail) Future(InvalidInfo("Error A").left) else Future(Response("ComponentA Success").right)
    }
  }
  class ComponentB {
    def bar(fail: Boolean): Future[\/[InvalidInfo, Response]] = {
      if(fail) Future(InvalidInfo("Error B").left) else Future(Response("ComponentB Success").right)
    }
  }

  val a = new ComponentA
  val b = new ComponentB

  val result = for {
    resultA <- EitherT(a.foo(false))
    resultB <- EitherT(b.bar(false))
  } yield (resultA, resultB)

  println(Await.result(result.run))
}

You could extend the Future class by implicitly adding a method that handles Either, instead of having to match it by yourself every time:

implicit class EitherHandlingFuture[Exception, Value](future: Future[Either[Exception, Value]]) {
  def mp[Return](fn: Value => Return) = {
    future.map { eth: Either[Exception, Value] =>
      eth match {
        case Left(ex: Exception) => { print("logging the exception") /* handle or rethrow */ }
        case Right(res: Value) => fn(res)
      }
    }
  }
}

Then, this would be possible:

def someComputation: Future[Either[Exception, Int]] = Future.value(Right(3))

someComputation mp { res: Int =>
  println(res)
}

Note that the snippet above doesn't play with for comprehensions, because to support them, it would be necessary to fully implement map/flatMap. For that, you'd probably want to subclass Future.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!