EDIT
Turns out, it's a very well known issue, it is caused by SI-2712. If you add the sbt-partial-unification plugin to your project, your original code will work just fine.
As I said, |@|
is deprecated, you should change for the following syntax:
import cats.syntax.apply._
(OptionT(foo), OptionT(bar)).mapN(_ + _)
There seems to be an implicit resolution issue. OptionT
has a Monad
instance, and Monad
extends Apply
which extends Cartesian
so your code should work indeed. It does work if you help the compiler a little bit:
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(_ + _)
}
but if you don't specify the types for catsSyntaxCartesian
you get an interesting error:
[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(_ + _)
Note that |@|
is now deprecated but it looks like you will have the same issue with its replacement mapN
.
I can think of two workarounds. If you want to use OptionT
, then bring its Apply
instance in scope and use map2
directly (you might want to use a type alias instead of a type lambda):
import cats.Apply
Apply[({type λ[α] = OptionT[Future, α]})#λ].map2(OptionT(foo), OptionT(bar))(_ + _)
Alternatively, you can drop OptionT
altogether and use Apply.compose
:
import cats.instances.option._
Apply[Future].compose[Option].map2(foo, bar)(_ + _)
The future will be executed in parallel in that case though, so watch out if that's not what you want.