简体   繁体   中英

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

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] :

  • by invoking cats.instances.future.catsStdInstancesForFuture
  • by invoking 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] .

The problem is that the instances are imported twice, meaning scalac cannot disambiguate between them and doesn't know which one to use and then fails.

So either you use the implicits._ import or you import specific instances with instances.<datatype>._ , but never both!

You can look at a more in depth look of cats imports here: https://typelevel.org/cats/typeclasses/imports.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM