OptionT的笛卡尔积累器

时间:2017-09-06 09:47:04

标签: scala scala-cats

我想加入2 Future[Option[_]]

def foo: Future[Option[Int]] = ???
def baz: Future[Option[Int]] = ???

可以加入一对Future

foo |@| baz map( (fooOpt, bazOpt) => ???)

可以加入一对Option

Option(1) |@| Option(2) map ( (a, b) => ???)

如何加入OptionT对?不是这样的:

OptionT(foo) |@| OptionT(baz) map ( (a, b) => ???)

UPD - 这是我的导入:

import cats.data.OptionT
import cats.instances.future._
import cats.instances.option._
import cats.syntax.cartesian._

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

1 个答案:

答案 0 :(得分:1)

修改

原来,这是一个众所周知的问题,它是由SI-2712引起的。如果您将sbt-partial-unification插件添加到项目中,原始代码就可以正常运行。

正如我所说,|@|已被弃用,您应该根据以下语法进行更改:

import cats.syntax.apply._
(OptionT(foo), OptionT(bar)).mapN(_ + _)

似乎存在隐含的解决方案问题。 OptionTMonad个实例,Monad扩展Apply扩展Cartesian,因此您的代码应该可以正常工作。如果您帮助编译器一点

,它确实有效
import scala.concurrent.{ExecutionContext, Future}

import cats.data.OptionT
import cats.syntax.cartesian._
import cats.instances.future._

trait CartesianOptionTs  {

  implicit def ec: ExecutionContext

  def foo: Future[Option[Int]]
  def bar: Future[Option[Int]]

  (catsSyntaxCartesian[({type λ[α] = OptionT[Future, α]})#λ, Int](OptionT(foo)) |@| OptionT(bar)).map(_ + _)
}

但如果您没有为catsSyntaxCartesian指定类型,则会收到一个有趣的错误:

[error]  found   : cats.data.OptionT[scala.concurrent.Future,Int]
[error]  required: ?F[?A]
[error] Note that implicit conversions are not applicable because they are ambiguous:
[error]  both method ArrowAssoc in object Predef of type [A](self: A)ArrowAssoc[A]
[error]  and method Ensuring in object Predef of type [A](self: A)Ensuring[A]
[error]  are possible conversion functions from cats.data.OptionT[scala.concurrent.Future,Int] to ?F[?A]
[error]       (catsSyntaxCartesian(OptionT(foo)) |@| OptionT(bar)).map(_ + _)

请注意,|@|现已弃用,但您的替代mapN似乎会遇到同样的问题。

我可以想到两个解决方法。如果要使用OptionT,请将其Apply实例放在范围内并直接使用map2(您可能希望使用类型别名而不是类型lambda):

  import cats.Apply
  Apply[({type λ[α] = OptionT[Future, α]})#λ].map2(OptionT(foo), OptionT(bar))(_ + _)

或者,您可以完全删除OptionT并使用Apply.compose

  import cats.instances.option._
  Apply[Future].compose[Option].map2(foo, bar)(_ + _)

在这种情况下,未来将同时执行,所以请注意这不是你想要的。