Why are there two parameter groups in cats Bifunctor[F[_, _]].bimap function?

Flo*_*erl 0 functional-programming scala scala-cats

I stumbled upon this problem when trying to implement a Bifunctior type class for maps (Bifunctor[Map[_, _]]).

Bifunctor is defined like this in cats:

/**
   * The quintessential method of the Bifunctor trait, it applies a
   * function to each "side" of the bifunctor.
   *
   * Example:
   * {{{
   * scala> import cats.implicits._
   *
   * scala> val x: (List[String], Int) = (List("foo", "bar"), 3)
   * scala> x.bimap(_.headOption, _.toLong + 1)
   * res0: (Option[String], Long) = (Some(foo),4)
   * }}}
   */
  def bimap[A, B, C, D](fab: F[A, B])(f: A => C, g: B => D): F[C, D]
Run Code Online (Sandbox Code Playgroud)

如注释所述,可以使用两个函数(在一个参数组中)作为其输入来调用此函数,如下所示:x.bimap(_.headOption, _.toLong + 1)。这告诉我,这显然不是bimap要调用的函数,因为该函数具有两个参数组((fab: F[A, B])(f: A => C, g: B => D))。我一直想知道是否存在某种我不知道在这里发生的隐式类型转换。它是如何工作的?我需要实现什么才能获取地图的Bifunctor类型类?

Dmy*_*tin 5

型类的实例BifunctorMap可被定义如下

implicit val mapBifunctor: Bifunctor[Map] = new Bifunctor[Map] {
  override def bimap[A, B, C, D](fab: Map[A, B])(f: A => C, g: B => D): Map[C, D] = 
    fab.map { case (k, v) => f(k) -> g(v) }
}
Run Code Online (Sandbox Code Playgroud)

x.bimap(_.headOption, _.toLong + 1)隐式中解析两次:

  • 首先,Bifunctor[Tuple2]找到实例(import cats.instances.tuple._or import cats.instances.all._import cats.implicits._),

  • 其次,扩展方法被解决(import cats.syntax.bifunctor._import cats.syntax.all._import cats.implicits._

因此x.bimap(_.headOption, _.toLong + 1)变成

implicitly[Bifunctor[Tuple2]].bimap(x)(_.headOption, _.toLong + 1)
Run Code Online (Sandbox Code Playgroud)

要么

Bifunctor[Tuple2].bimap(x)(_.headOption, _.toLong + 1)
Run Code Online (Sandbox Code Playgroud)

要么

toBifunctorOps(x)(catsStdBitraverseForTuple2).bimap(_.headOption, _.toLong + 1)
Run Code Online (Sandbox Code Playgroud)