斯卡拉猫,笛卡尔上的平面地图?



给定:

import cats.syntax.cartesian._
type M[X] = Future[Either[Error, X]]
val ma: M[A] = ???
val mb: M[B] = ???

我知道我可以做到:

def doStuff(a: A, b: B): C = ???
val result: M[C] = (ma |@| mb).map(doStuff)

但是我如何平面地图?CartesianBuilder没有flatMap

def doFancyStuff(a: A, b: B): M[C] = ???
val result: M[C] = (ma |@| mb).flatMap(doFancyStuff)

我认为主要问题是在EitherT[Future, Error, X]-monad 堆栈的意义上flatMapFuture[Either[Error, X]]很尴尬,因为Future的原始flatMap妨碍了,并且编译器没有寻找可以同时处理FutureEither组合的monad实例。但是,如果您将期货包装在EitherT中,一切都很顺利。

对于猫 1.0.1

在下文中,(a,b).tupled对应于Cartesian.product(a, b)(a, b).mapN对应于已弃用的(a |@| b).map

给定类型ABCError,以下代码片段在 cats 1.0.1 下编译:

如果你想保留你对M的定义(你可能应该),那么你 可以通过将所有内容包装在EitherT中来避免上述问题,然后 提取value

import scala.util.Either
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global // required by Monad[Future]
import cats.instances.future._                            // Monad for `Future`
import cats.syntax.apply._                                // `tupled` and `mapN`
import cats.data.EitherT                                  // EitherT monad transformer
type M[X] = Future[Either[Error, X]]
val ma: M[A] = ???
val mb: M[B] = ???
def doStuff(a: A, b: B): C = ???
val result1: M[C] = (EitherT(ma), EitherT(mb)).mapN(doStuff).value
def doFancyStuff(a: A, b: B): M[C] = ???
val result2: M[C] = (for {
ab <- (EitherT(ma), EitherT(mb)).tupled
c <- EitherT(doFancyStuff(ab._1, ab._2))
} yield c).value

但是,如果这看起来太尴尬,可以调整M的定义, 以下变体可能稍短一些:

import scala.util.Either
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global // required by Monad[Future]
import cats.instances.future._                            // Monad for `Future`
import cats.syntax.apply._                                // `tupled` and `mapN`
import cats.data.EitherT                                  // EitherT monad transformer
type M[X] = EitherT[Future, Error, X]
val ma: M[A] = ??? // either adjust signatures, or wrap result in EitherT(res)
val mb: M[B] = ???
def doStuff(a: A, b: B): C = ???
val result1: M[C] = (ma, mb).mapN(doStuff)
def doFancyStuff(a: A, b: B): M[C] = ???
val result2: M[C] = (ma, mb).tupled.flatMap{
case (a, b) => doFancyStuff(a, b)
}

这是因为(ma, mb).tupled构建了一个M[(A, B)],其中M是前面提到的monad堆栈,然后可以很容易地用(A, B) => M[C]函数flatMapM[C]

对于具有Cartesian的旧版本(未经测试)

假设(ma, mb).tupled对应于已弃用的Cartesian.product(ma, mb).mapN对应于已弃用的(ma |@| mb).map,则上述 1.0.1 代码片段中result1result2的两个定义转换为:

val result1: M[C] = (ma |@| mb).map(doStuff)
val result2: M[C] = Cartesian[M].product(ma, mb).flatMap{ 
case (a, b) => doFancyStuff(a, b) 
}

同样,这仅Cartesian[M].product(ma, mb)M[A]M[B]构建了一个M[(A, B)],其中M[X]被定义为EitherT[Future, Error, X]。如果它被定义为Future[Either[Error, X]],那么flatMap将在Future上被调用,而不是doFancyStuff我们必须传递类似Either[Error, (A, B)] => Future[Either[Error, C]]的东西,这可能不是你想要的。

相关内容

  • 没有找到相关文章

最新更新