簡體   English   中英

結合期貨(Twitter)和Scala中的任何一種

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

我們使用Twitter期貨(作為Finagle堆棧的一部分),我不喜歡使用(業務)異常來控制應用程序流的概念,因為異常不會出現在方法簽名中。

所以我有了使用Future [Aither [A,B]]作為替代品的想法。

但是我使用這個概念來使用對期貨的理解存在一些問題:

例如,我們有一個存儲庫方法:

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

和一個處理程序方法,它使用這個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

getUserCredentialsByNickname(..)之后的for comprehension中的調用只應在此調用返回Right [UserCredentials]時執行,而且應從處理程序返回每個返回的Either的詳細錯誤信息。

所以現在我已經嘗試使用Scalaz Either(這是一個正確的偏向Either與中性scala Either相比)和Monad Transformer EitherT,它似乎完全符合我的要求。 感謝Huw,特別是Lars Hupel暗示我正確的方向。

這里有一個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))
}

您可以通過隱式添加一個處理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)
      }
    }
  }
}

然后,這將是可能的:

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

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

請注意,上面的代碼段並不for理解,因為為了支持它們,有必要完全實現map / flatMap。 為此,你可能想要繼承Future

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM