I'm doing some staff in Scala and came across a problem with implicit
instances. Let's consider the following example:
import cats.{Applicative, FlatMap, Monad}
import cats.syntax.functor._
import cats.syntax.flatMap._
import cats.syntax.applicative._
class Test[F[_]: Monad] extends App{
val t1 = ().pure[F]
val t2 = ().pure[F]
def testFlatApplicative: F[Unit] =
for{
_ <- t1
_ <- t2
} yield ()
}
This compiles fine. But since cats.Monad[F[_]]
is declared as follows:
@typeclass trait Monad[F[_]] extends FlatMap[F] with Applicative[F]
I expected the following to work as well
import cats.{Applicative, FlatMap, Monad}
import cats.syntax.functor._
import cats.syntax.flatMap._
import cats.syntax.applicative._
class Test[F[_]: FlatMap : Applicative] extends App{
val t1 = ().pure[F]
val t2 = ().pure[F]
def testFlatApplicative: F[Unit] =
for{
_ <- t1
_ <- t2
} yield ()
}
But it fails to compile with error:
Error:(16, 12) value map is not a member of type parameter F[Unit]
_ <- t2
This is strange. Apply
extends Functor
...
Can anyone explain this behavior?
class Test[F[_] : FlatMap : Applicative]
is desugared to
class Test[F[_]](implicit flatMap: FlatMap[F], applicative: Applicative[F])
If you desugar for-comprehension and syntaxes you'll see the problem:
def testFlatApplicative: F[Unit] =
FlatMap[F].flatMap(t1)(_ =>
Functor[F].map(t2)(_ => ())
)
Error: ambiguous implicit values:
both value applicative in class Test of type cats.Applicative[F]
and value flatMap in class Test of type cats.FlatMap[F]
match expected type cats.Functor[F]
So you can resolve ambiguity manually:
def testFlatApplicative: F[Unit] =
FlatMap[F].flatMap(t1)(_ =>
applicative.map(t2)(_ => ())
)
or
def testFlatApplicative: F[Unit] =
FlatMap[F].flatMap(t1)(_ =>
flatMap.map(t2)(_ => ())
)
There is no such problem when you write class Test[F[_]: Monad]
since you have single implicit.