对于我的示例,我将使用Option
s,但在我的实际代码中,我使用的是自定义数据类型.添加导入import cats.std.option._
将解决示例中的问题.我有一些看起来像这样的代码:
import cats.{FlatMap, Eval, Applicative} import cats.data.Kleisli import cats.syntax.all._ implicit val optionApplicative = new Applicative[Option] { override def pure[A](x: A): Option[A] = Option(x) override def ap[A, B](fa: Option[A])(f: Option[(A) => B]): Option[B] = for { a <- fa fUnwrapped <- f } yield fUnwrapped(a) } val test : Option[(Int, String)] = (Option(4) |@| Option("name")).map((_, _))
这段代码编译运行得很好.
接下来我曾经Kleisli
写过一些返回的函数Option
:
val test2 : List[Int] => Option[Int] = { val f = (xs : List[Int]) => xs.headOption val g = (x : Int) => Option(x) Kleisli(f).andThen(g).run }
代码没有编译,因为我的数据类型没有FlatMap
实例.我创建了一个:
implicit val optionFlatmap = new FlatMap[Option] { override def flatMap[A, B](fa: Option[A])(f: (A) => Option[B]): Option[B] = fa.flatMap(f) override def map[A, B](fa: Option[A])(f: (A) => B): Option[B] = fa.map(f) }
导入FlatMap
实例(或在同一文件中定义实例)后,构建器语法不再编译.我收到错误:
error: value |@| is not a member of Option[Int] [ERROR] val test : Option[(Int, String)] = (Option(4) |@| Option("name")).map((_, _)) [ERROR] ^
为什么导入FlatMap
实例会破坏构建器语法?我怎样才能解决这个问题?
使用-Xlog-implicits
编译器标志对此进行调试很有帮助,该标志将告诉您编译器尝试了哪些含义,以及它们失败的原因.在您的情况下,第一条消息(至少对我而言)解释了它:
scala> (Option(4) |@| Option("name")).map((_, _)):20: applySyntax is not a valid implicit value for Option[Int] => ?{def |@|: ?} because: ambiguous implicit values: both value optionApplicative of type => cats.Applicative[Option] and value optionFlatmap of type => cats.FlatMap[Option] match expected type cats.Apply[Option] (Option(4) |@| Option("name")).map((_, _))
要使apply操作起作用,需要隐式Apply[Option]
,但需要Applicative
和FlatMap
扩展Apply
,因此编译器不知道选择哪一个.简单的解决方案是在这两个实例合并成的一个实例Monad
,这两个延伸Applicative
和FlatMap
.
import cats._ import cats.data.Kleisli import cats.syntax.all._ implicit val optionMonad = new Monad[Option] { def pure[A](x: A): Option[A] = Option(x) def flatMap[A, B](fa: Option[A])(f: (A) => Option[B]): Option[B] = fa.flatMap(f) }
现在一切正常:
scala> val test : Option[(Int, String)] = (Option(4) |@| Option("name")).map((_, _)) test: Option[(Int, String)] = Some((4,name)) scala> val test2 : List[Int] => Option[Int] = { val f = (xs : List[Int]) => xs.headOption val g = (x : Int) => Option(x) Kleisli(f).andThen(g).run } test2: List[Int] => Option[Int] =