2013-07-24 136 views
17

我有一個應用程序可以對不同的後端系統進行大量調用,希望能夠使用理解來簡化後端系統中的流程。將EitherT和Future結合起來

我期待結合EitherT(scalaz)和Future(scala 2.10),這樣我就可以捕獲第一個潛在錯誤(其未來或後端系統問題),並向最終用戶返回適當的消息。我已經快速查看了一個scalaz驗證,但是捕獲第一個錯誤而不是所有錯誤的建議是使用EitherT。

我想在REPL一個簡單的例子,第一個,但我發現了以下錯誤

錯誤:無法找到參數F隱含值:scalaz.Functor [scala.concurrent.Future]

import scala.concurrent._ 
import scalaz._ 
import Scalaz._ 
import ExecutionContext.Implicits.global 

type EitherFuture[+A] = EitherT[Future, String, A] 

def method1Success : EitherFuture[Int] = { 
    println("method 1 success") 
    EitherT { 
    Future { 
     1.right 
    } 
    } 
} 

def method2Failure : EitherFuture[Int] = { 
    println("method 2 failure") 
    EitherT { 
    Future { 
     "fail".left 
    } 
    } 
} 

val m1 = method1Success 

// problem 
m1.isRight 

// problem 
def methodChain1 = { 
    for { 
    a <- method1Success 
    b <- method2Failure 
    } yield b 
} 

我對scala和scalaz都很陌生,所以任何指針都會很棒。

** **更新

通過包括基於@stew建議scalaz-contrib請我現在有一個更新的版本顯示,內涵與合併EitherT和未來呈現不同的簡單的用例後端的成功,後端失敗,以及未來失敗

import scala.concurrent._ 
import scalaz._ 
import Scalaz._ 
import ExecutionContext.Implicits.global 
import scalaz.contrib._ 
import scalaz.contrib.std._ 
import scala.concurrent.duration._ 

type EitherFuture[+A] = EitherT[Future, String, A] 

// various methods that mimic success or different failures 
def methodBackendSuccess : EitherFuture[Int] = { 
    println("method backend success") 
    EitherT { 
    Future {1.right} 
    } 
} 

def methodBackendFailure : EitherFuture[Int] = { 
    println("method backend failure") 
    EitherT { 
    Future { "fail".left} 
    } 
} 

def methodFutureFailure : EitherFuture[Int] = { 
    println("method future failure") 
    EitherT { 
    Future.failed(new Exception("future failed")) 
    } 
} 

// different combinations for for-comprehensions 
def methodChainBackendSuccess = { 
    for { 
    a <- methodBackendSuccess 
    b <- methodBackendSuccess 
    c <- methodBackendSuccess 
    } yield c 
} 

def methodChainBackendFailure = { 
    for { 
    a <- methodBackendSuccess 
    b <- methodBackendFailure 
    c <- methodBackendSuccess 
    } yield c 
} 

def methodChainFutureFailure = { 
    for { 
    a <- methodBackendSuccess 
    b <- methodFutureFailure 
    c <- methodBackendSuccess 
    } yield c 
} 

// process results for different chain methods 
def processOutcome(chainMethod: => EitherFuture[Int]):Int = try { 
    val x = Await.result(chainMethod.run, 30 seconds) 
    x.toEither match {        
     case Left(l) => { 
     println("Backend failure <" + l + ">") 
     -1 
     } 
     case Right(r) => { 
     println("Backend success <" + r + ">") 
     r 
     } 
    } 
    } catch { 
    case e: Exception => { 
     println("Future error <" + e.getMessage + ">") 
     -99 
    } 
} 

// run tests 
val backendSuccess = processOutcome(methodChainBackendSuccess) 
val backendFailure = processOutcome(methodChainBackendFailure) 
val futureFailure = processOutcome(methodChainFutureFailure) 
+0

你確定你需要'Either'嗎? 「未來」已經可以模擬失敗,並且排序具有您想要的行爲。 –

+0

Hi @TravisBrown我仍然對scala很感興趣,所以你可能是正確的,我需要的是能夠捕獲後端成功,後端失敗和未來失敗,並以不同的方式處理它們。我現在已經有了一些可行的代碼,我會更新原始問題,也許這可能會讓我更清楚是否需要組合EitherT和Future。 –

+1

有一件事花了我一段時間:Functor [Future]只能在隱式執行上下文中找到 – cvogt

回答

6

您需要爲未來導入或提供Functor實例。我建議使用scalaz-contrib項目中的那個。 -contrib是一個獨立的項目,由在scalaz上工作的同一個人工作。 Future實例在這個包中,而不是scalaz-core,因爲現在scalaz-core保持了scala 2.9和2.10之間的兼容性。

+0

通過添加'「org.typelevel」%%「scalaz-contrib-210」%「0.1.4」'到我的Build.scala文件我能夠更新我的示例使其工作,我將更新原始問題碼。 –

1

看的isRight上EitherT定義簽名:

def isRight(implicit F: Functor[F]): F[Boolean] 

它期望一個Functor參數化你的EitherT的類型參數,在你的案例Future中。 Scalaz不提供針對未來型的隱函子,你需要編寫自己下面這個模型:

http://scalaz.github.io/scalaz/scalaz-2.9.0-1-6.0/doc.sxr/scalaz/Functor.scala.html

通知所有每個支持類型的隱含DEFS。

+1

因此,如果我使用Scalaz 6,它會像添加隱式def FutureFunctor:Functor [Future] = new Functor [Future] def fmap [A​​,B](t:Future [A],f: A => B):Future [B] = t map f不幸的是,我正在與Scalaz 7合作,隱含的defs似乎已經移動了。我應該在Scalaz7中尋找類似的東西? –