为什么添加 import `import cats.instances.future._` 会导致隐式 Functor[Future] 的编译错误

Why adding import `import cats.instances.future._` will result an compilation error for implicit Functor[Future]

scala 代码使用猫并且运行良好:

import cats.implicits._
import cats.Functor
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

object Hello extends App {

  Functor[Future].map(Future("hello"))(_ + "!")

}

但是如果我添加这个导入:

import cats.instances.future._

会报这样的编译错误:

 Error:(18, 10) could not find implicit value for parameter instance: cats.Functor[scala.concurrent.Future]
   Functor[Future].map(Future("hello"))(_ + "!")

为什么会发生,我该如何调试它来找到原因?我用了各种我知道的方法,还是找不到。

build.sbt 文件是:

name := "Cats Implicit Functor of Future Compliation Error Demo"

version := "0.1"

organization := "org.my"

scalaVersion := "2.12.4"

sbtVersion := "1.0.4"

libraryDependencies ++= Seq(
  "org.typelevel" %% "cats-core" % "1.0.1"
)

问题是实例被导入了两次,这意味着 scalac 无法消除它们之间的歧义并且不知道使用哪一个然后失败。

因此,要么使用 implicits._ 导入,要么使用 instances.<datatype>._ 导入特定实例,但绝不能同时使用!

您可以在此处更深入地了解猫的进口情况:https://typelevel.org/cats/typeclasses/imports.html

作为线性超类型的对象 cats.implicits has the FutureInstances traitFutureInstances 有一个隐式的 catsStdInstancesForFuture 方法,它产生一个 Monad[Future],而后者又是一个 Functor[Future].

另一方面,对象cats.instances.future 混入了FutureInstances,所以它又提供了一个隐式方法catsStdInstancesForFuture,但是通过另一条途径。

现在编译器有两种可能生成 Functor[Future]:

  • 通过调用 cats.instances.future.catsStdInstancesForFuture
  • 通过调用 cats.implicits.catsStdInstancesForFuture

由于无法决定选择哪一个,因此会退出并显示一条错误消息。

为避免这种情况,请不要将 cats.implicits._cats.instances.future._ 一起使用。要么省略其中一个导入,要么使用

`import packagename.objectname.{member1name, member2name}`

到 select 只有那些你需要的暗示。


"-print" 添加到 scalacOptions 可以 帮助调试隐式:

scalacOptions ++= Seq(
  ...
  "-print",
  ...
)

它将打印出脱糖后的代码,并在各处添加 cats.implicits.cats.instances. 块。不幸的是,它往往会产生很多噪音。


发生这种情况的更根本原因是无法在导致 [= 的两个(等效)路径之间定义 higher-dimensional-cells(kind-of "homotopies") 17=]。如果我们有可能告诉编译器采用哪条路径并不重要,那么一切都会好得多。由于我们做不到,所以我们必须确保始终只有一种方法可以生成隐式 Functor[Future].