问题
I know that I can compose Apply
to work with nested structures, like in
def mapAll[A, B, C, D](o1: List[Option[A]],
o2: List[Option[B]],
o3: List[Option[C]])
(f: (A, B, C) => D)
: List[Option[D]] = {
import cats._
import cats.implicits._
Apply[List].compose[Option].map3(o1, o2, o3)(f)
}
However, is there a way to convince the compiler to accept (o1, o2, o3).mapN(f)
instead of Apply[List].compose[Option].map3(o1, o2, o3)(f)
, so that mapN
gets applied using the composed Apply
?
回答1:
This is exactly what cats.data.Nested
is for:
def mapAll[A, B, C, D](o1: List[Option[A]],
o2: List[Option[B]],
o3: List[Option[C]])
(f: (A, B, C) => D)
: List[Option[D]] = {
import cats.data.Nested
import cats.instances.list._, cats.instances.option._
import cats.syntax.apply._
(Nested(o1), Nested(o2), Nested(o3)).mapN(f).value
}
(Note that you will need to enable the -Ypartial-unification
compiler flag for the code above to compile. Alternatively you could add some type parameters, but off the top of my head I'm not sure exactly where they'd be required, and if you're doing anything with Cats -Ypartial-unification
is pretty much necessary, anyway.)
You could also just make the composed instance available implicitly:
import cats.Apply
import cats.instances.list._, cats.instances.option._
import cats.syntax.apply._
type ListOption[x] = List[Option[x]]
implicit val listOptionApply: Apply[ListOption] = Apply[List].compose[Option]
def mapAll[A, B, C, D](o1: ListOption[A],
o2: ListOption[B],
o3: ListOption[C])
(f: (A, B, C) => D)
: List[Option[D]] = (o1, o2, o3).mapN(f)
This really isn't ideal, though—it's non-standard and extremely fragile (e.g. the type aliases are necessary to guide resolution).
In my view your best bet is just to write out Apply[List].compose[Option]
explicitly and skip the fancy tuple syntax, but if you really have to have the fancy tuple syntax, go with Nested
.
来源:https://stackoverflow.com/questions/55199931/mapn-over-composed-apply