The scala code is using cats and works well:
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"))(_ + "!")
}
But if I add this import:
import cats.instances.future._
It will report such compilation errors:
Error:(18, 10) could not find implicit value for parameter instance: cats.Functor[scala.concurrent.Future]
Functor[Future].map(Future("hello"))(_ + "!")
Why it happens, and how can I debug it to find reason? I used all kinds of ways I know, but can't find anything.
The build.sbt
file is:
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"
)
The object cats.implicits
has the FutureInstances
trait as a linear supertype. The FutureInstances
has an implicit catsStdInstancesForFuture
method, which produces a Monad[Future]
, which in turn is a Functor[Future]
.
On the other hand, the object cats.instances.future
also mixes in FutureInstances
, so it again provides an implicit method catsStdInstancesForFuture
, but through another pathway.
Now the compiler has two possibilities to generate a Functor[Future]
:
cats.instances.future.catsStdInstancesForFuture
cats.implicits.catsStdInstancesForFuture
Since it cannot decide which one to take, it exits with an error message.
To avoid that, don't use cats.implicits._
together with cats.instances.future._
. Either omit one of the imports, or use the
`import packagename.objectname.{member1name, member2name}`
to select only those implicits that you need.
Adding "-print"
to scalacOptions
could help when debugging implicits:
scalacOptions ++= Seq(
...
"-print",
...
)
It will print out the desugared code with cats.implicits.
and cats.instances.
-pieces added everywhere. Unfortunately, it tends to produce quite a lot of noise.
The more fundamental reason why this happens is that there is no way to define higher-dimensional-cells (kind-of "homotopies") between the two (equivalent) pathways that lead to a Functor[Future]
. If we had a possibility to tell the compiler that it doesn't matter which path to take, then everything would be much nicer. Since we can't do it, we have to make sure that there is always only one way to generate an implicit Functor[Future]
.