Question

I have an app that does a lot of calls to different backend systems, and hoping to use for-comprehensions to simplify the process flow across the backend systems.

I'm looking to combine EitherT (scalaz) and Future (scala 2.10) so I can capture the first potential error (where its a future or backend system issue) and return an appropriate message to the end user. I've had a quick look a scalaz Validation but the recommendation for capturing first error and not all errors is to use EitherT.

I'm trying a simple example in REPL first, however I'm getting the following error

error: could not find implicit value for parameter 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
}

I'm still new to both scala and scalaz so any pointers would be great.

** Update **

By including scalaz-contrib based on @stew suggestion I now have an updated version that shows for-comprehensions with the combined EitherT and Future showing different simple use cases backend success, a backend failure, and a future failure

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)
Was it helpful?

Solution

You need to either import or provide a Functor instance for Future. I recommend using the one from the scalaz-contrib project. -contrib is a separate project worked on by the same people that work on scalaz. The Future instances are in this package instead of scalaz-core because scalaz-core, for now, maintains compatibility between scala 2.9 and 2.10.

OTHER TIPS

Look at the signature of isRight defined on EitherT:

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

It is expecting a Functor parameterized with the type parameter of your EitherT, in your case Future. Scalaz does not provide an implicit Functor for the type Future, you need to write your own following this model:

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

Notice all the implicit defs for each supported type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top