简体   繁体   English

结合期货(Twitter)和Scala中的任何一种

[英]Combining Futures (Twitter) and Either in Scala

We use Twitter futures (as part of the Finagle stack) and I don't like the concept of using (business) exceptions to control the flow of our application, because exceptions don't show up in method signatures. 我们使用Twitter期货(作为Finagle堆栈的一部分),我不喜欢使用(业务)异常来控制应用程序流的概念,因为异常不会出现在方法签名中。

So I had the idea to use Future[Either[A,B]] as a replacement. 所以我有了使用Future [Aither [A,B]]作为替代品的想法。

But I have some problems in using for comprehensions over futures with this concept: 但是我使用这个概念来使用对期货的理解存在一些问题:

Eg we have a repository method: 例如,我们有一个存储库方法:

def getUserCredentialsByNickname(nickname: String): Future[Either[EntityNotFound, UserCredentials]]

and a handler method which uses this repo and does some other checks and also creates a token 和一个处理程序方法,它使用这个repo并做一些其他检查,并创建一个令牌

def process(request: LoginRequest): Future[Either[Failure, Login]] = {
      for {
        credentialsEither <- userRepository.getUserCredentialsByNickname(request.username)
        ...several other calls/checks which should 'interrupt' this for comprehension
        token <- determineToken(credentials)
} yield token

The calls in the for comprehension after the getUserCredentialsByNickname(..) should only be executed if this call returns a Right[UserCredentials], but also the detailed error information from each returned Either should be returned from the handler. getUserCredentialsByNickname(..)之后的for comprehension中的调用只应在此调用返回Right [UserCredentials]时执行,而且应从处理程序返回每个返回的Either的详细错误信息。

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. 所以现在我已经尝试使用Scalaz Either(这是一个正确的偏向Either与中性scala Either相比)和Monad Transformer EitherT,它似乎完全符合我的要求。 Thanks to Huw and especially Lars Hupel for hinting me in the right direction. 感谢Huw,特别是Lars Hupel暗示我正确的方向。

Here is working a sample for Twitter futures and Scalaz Either and EitherT: 这里有一个Twitter期货和Scalaz Either和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: 您可以通过隐式添加一个处理Either的方法来扩展Future类,而不是每次都必须自己匹配它:

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理解,因为为了支持它们,有必要完全实现map / flatMap。 For that, you'd probably want to subclass Future . 为此,你可能想要继承Future

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM