2

对于我的示例,我将使用Options 但在我的实际代码中我使用的是自定义数据类型。添加导入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实例会破坏构建器语法?我怎样才能解决这个问题?

4

1 回答 1

5

使用-Xlog-implicits编译器标志进行调试会很有帮助,它会告诉您编译器尝试了哪些隐式,以及它们失败的原因。就您而言,第一条消息(至少对我而言)解释了它:

scala> (Option(4) |@| Option("name")).map((_, _))
<console>: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[Option]函数,但同时需要ApplicativeFlatMap扩展Apply,因此编译器不知道该选择哪一个。对此的简单解决方案是将两个实例组合成一个 的实例Monad,该实例同时扩展ApplicativeFlatMap

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] = <function1>
于 2015-12-20T00:33:02.300 回答