1

例如,我们有一些带有不同“容器”的服务,Future并且Option

//first service with Future
class FirstService { 
      getData(): XorT[Future, ServiceError, SomeData]
}

//second service with Optin
class SecondService {
      getData(): XorT[Option, ServiceError, SomeData]
}

我们如何将它们结合起来使用一个来进行理解以避免类型不匹配?

val result = for {
    data1 <- firstService.getData()
    data2 <- secondService.getData() // type mismatch required XorT[Future, ServiceError, SomeData]
} yield mergeResult(data1, data2)
4

2 回答 2

2

XorT[F, A, B]只是一个方便的包装器F[A Xor B],所以你的问题本质上是:如何结合 aFuture和 an Option。因为你还是要以某种形式返回a Future,这主要变成:如何处理Option.

有几种可能性:

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import cats.data.XorT
import cats.implicits._

type ServiceError = String
type FutureErrorOr[A] = XorT[Future, ServiceError, A]

val fXorT: FutureErrorOr[Int] = XorT.right(Future.successful(1))
val oXorT: XorT[Option, ServiceError, Int] = XorT.right(1.some)
  • Option变成Future( Noneto Future.failed) :

    val opt2fut: FutureErrorOr[Int] = 
      XorT(oXorT.value.fold(
        Future.failed[ServiceError Xor Int](new NoSuchElementException())(
        Future.successful _))
    
    for { a <- fXort; b <- opt2fut } yield a + b
    
  • Option变成ServiceError Xor ?( Noneto Xor.Left) :

    val opt2xor: FutureErrorOr[Int] = 
      XorT.fromXor[Future](oXorT.value.getOrElse("no elem".left))
    
    for { a <- fXort; b <- opt2xor } yield a + b
    
  • 将您的返回类型更改为XorT[Future, ServiceError, Option[X]](如果您需要X在 for 理解的其余部分中使用,这可能没有用):

    val optInside: FutureErrorOr[Option[Int]] = 
      XorT.fromXor[Future](oXorT.value.sequenceU)
    
    for { a <- fXorT; b <- optInside } yield b.map(_ + a)
    
于 2016-07-17T13:08:40.773 回答
1

解决此问题的一种可能方法是使Container不同类型的通用 monad ( Future, Option):

trait Container[+A] {
  def map[B](f: A => B): Container[B]
  def flatMap[B](f: A => Container[B]): Container[B]
}
// Empty container for value
class EmptyContainer[+A](value: A) extends Container[A] {
  override def map[B](f: (A) => B): Container[B] = new EmptyContainer[B](f(value))
  override def flatMap[B](f: (A) => Container[B]): Container[B] = f(value)
}
// Implement container for Option
class OptionContainer[+A](option: Option[A]) extends Container[A] {
  override def map[B](f: (A) => B): Container[B] = new OptionContainer[B](option.map(f))
  override def flatMap[B](f: (A) => Container[B]): Container[B] = option match {
    case Some(value) => f(value)
    case None => new OptionContainer[B](None)
  }
}
// Implement container for Future
class FutureContainer[+A](future: Future[A]) extends Container[A] {
  override def map[B](f: (A) => B): Container[B] = new FutureContainer[B](future.map(f))
  // TODO: can be better!!!
  override def flatMap[B](f: (A) => Container[B]): Container[B] = {
    val promise = Promise[B]()
    future.onComplete {
      case Success(a) => f(a).map(b => promise.success(b))
      case Failure(exception) => promise.failure(exception)
    }
    new FutureContainer[B](promise.future)
  }
}

您可以为任何其他类型添加自己的实现。

// Monad for Container
object Container {
  implicit def monad = new Monad[Container] {
    def flatMap[A, B](fa: Container[A])(f: (A) => Container[B]): Container[B] = fa.flatMap(f)
    def pure[A](x: A): Container[A] = new EmptyContainer[A](x)
  }
}

我们的服务现在有视图:

class SomeContainerService {
  def getSomeDate(): XorT[Container, Error, SomeData] =
    XorT.right(Option(SomeData()).toContainer)

  def getRemoteDate(): XorT[Container, Error, SomeData] =
    XorT.right(Future(SomeData()).toContainer)
}

未来和选项的扩展方法:

def toContainer = OptionContainer(option)
def toContainer = FutureContainer(future)

并且为了理解工作正常:

val result: XorT[Container, Error, SomeData] = for {
  data1 <- someContainerService.getRemoteDate() // future
  data2 <- someContainerService.getSomeDate()   // option
} yield {
  mergeResult(data1, data2)
}
于 2016-07-26T19:13:43.030 回答