scala cats ambiguous implicit values

  • A+
Category:Languages
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.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: