2013-07-24 38 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中寻找类似的东西? –