import cats._
import cats.implicits._
trait Console[F[_]]{
def readInput() : F[Int]
def print(msg: String) : F[Unit]
}
class Foo {
def doFoo[F[_]: Monad](number: Int)(implicit C: Console[F]) : F[Unit] = {
C.readInput().flatMap{input =>
if (input == number) C.print("you won").map(_ => ())
else if (input > number) C.print("you guessed too high").flatMap(_ => doFoo(number))
else C.print("you guessed too low").flatMap(_ => doFoo(number))
}
}
}
But I get this cryptic error from the compiler
cmd18.sc:5: ambiguous implicit values:
both value catsStdInstancesForList in trait ListInstances of type => cats.Traverse[List] with cats.Alternative[List] with cats.Monad[List] with cats.CoflatMap[List]
and value catsStdInstancesForVector in trait VectorInstances of type => cats.Traverse[Vector] with cats.Monad[Vector] with cats.Alternative[Vector] with cats.CoflatMap[Vector]
match expected type cats.Monad[F]
else if (input > number) C.print("you guessed too high").flatMap(_ => dooFoo(number))
^
The problem is that you're asking too much of Scala's type inference. It's trying to figure out the type parameter it needs for doFoo[?](number)
, and while it's pretty clear to us as humans that it has to be F
given the context the expression doFoo(number)
appears in, the compiler isn't that smart.
The simplest solution is just to provide the type parameter explicitly:
.flatMap(_ => doFoo[F](number))
If you find that annoying, you can help the compiler out a bit by desugaring the F[_]: Monad
constraint bound so that you can make the order of the Console
and Monad
instances explicit:
import cats._
import cats.implicits._
trait Console[F[_]]{
def readInput() : F[Int]
def print(msg: String) : F[Unit]
}
class Foo {
def doFoo[F[_]](number: Int)(implicit C: Console[F], F: Monad[F]) : F[Unit] = {
C.readInput().flatMap{input =>
if (input == number) C.print("you won").map(_ => ())
else if (input > number) C.print("you guessed too high").flatMap(_ => doFoo(number))
else C.print("you guessed too low").flatMap(_ => doFoo(number))
}
}
}
In your original version, the Monad
context bound was getting desugared to an implicit Monad[F]
parameter that came before C: Console[F]
in the implicit parameter list, and so the compiler was trying to resolve it first. In the sugar-free version above I've reversed the order so that it will resolve the Console[F]
first, which will make everything work out just fine when the compiler gets around to trying to infer F
for the doFoo(number)
calls.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With