结合期货(Twitter)和Scala中的任何一种 [英] Combining Futures (Twitter) and Either in Scala

查看:87
本文介绍了结合期货(Twitter)和Scala中的任何一种的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们使用Twitter的期货(作为Finagle堆栈的一部分),我不喜欢使用(业务)异常来控制应用程序流程的概念,因为异常不会显示在方法签名中。



所以我有主意使用Future [Either [A,B]]代替。



使用这个概念来理解期货存在一些问题:



例如我们有一个存储库方法:

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

和一个处理程序方法,该方法使用此存储库并执行其他一些检查并还创建令牌

  def过程(请求:LoginRequest):Future [Either [Failure,Login]] = {
for {
凭据Either< -userRepository.getUserCredentialsByNickname(request.username)
...其他应通过打扰来理解的其他呼叫/检查
令牌<-defineToken(凭证)
}产生令牌

仅当此调用返回Right时,才执行对getUserCredentialsByNickname(..)之后的for理解的调用。 [UserCredentials],以及每个返回的Either的详细错误信息都应从处理程序中返回。

解决方案

所以我现在我曾尝试使用Scalaz Either(与中性标量Either相比是偏右的Either)和Monad Transformer EitherT,看来它确实满足了我的要求。



这里是Twitter期货以及Scalaz Either和EitherT的示例:

  import com.twitter.util。{等待,将来} 
导入scalaz。{Monad,Functor,EitherT,\ /}
import scalaz.syntax.ToIdOps

对象EitherTest使用ToIdOps {扩展应用程序{

//使Twitter期货与EitherT一起使用
隐式val FutureFunctor = new Functor [未来] {
def map [A​​,B](a:未来[A])(f:A => B):未来[B] =映射f
}
隐式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
}

//示例从此处开始:

案例类InvalidInfo(错误:字符串)
案例类Response(msg:字符串)


类ComponentA {
def foo(fail:布尔值):Future [\ / [Inva lidInfo,响应]] = {
if(fail)Future(InvalidInfo( Error A)。left)else Future(Response( ComponentA Success)。right)
}
}
类ComponentB {
def bar(失败:布尔值):Future [\ / [InvalidInfo,响应]] = {
if(失败)Future(InvalidInfo( Error B)。 left)else Future(Response( ComponentB Success)。right)
}
}

val a =新的ComponentA
val b =新的ComponentB

val结果=对于{
resultA<-EitherT(a.foo(false))
resultB<-EitherT(b.bar(false))
}收益(resultA,resultB)

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


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.

So I had the idea to use Future[Either[A,B]] as a replacement.

But I have some problems in using for comprehensions over futures with this concept:

E.g. 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

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.

解决方案

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))
}

这篇关于结合期货(Twitter)和Scala中的任何一种的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆