11
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))
    }
  }
} 

但是我从编译器中得到了这个神秘的错误

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))
                                                                        ^
4

1 回答 1

20

问题是您对 Scala 的类型推断要求太多。它试图找出它需要的类型参数doFoo[?](number),虽然我们人类很清楚它必须被F赋予表达式doFoo(number)出现的上下文,但编译器并不那么聪明。

最简单的解决方案就是显式提供类型参数:

.flatMap(_ => doFoo[F](number))

如果你觉得这很烦人,你可以通过对F[_]: Monad约束绑定去糖来帮助编译器,这样你就可以明确ConsoleandMonad实例的顺序:

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))
    }
  }
}

在您的原始版本中,Monad上下文绑定被取消为隐式参数列表Monad[F]中之前C: Console[F]出现的隐式参数,因此编译器首先尝试解析它。在上面的无糖版本中,我颠倒了顺序,以便它会解决第一个问题Console[F],这将使编译器开始尝试推断调用F时一切正常。doFoo(number)

于 2019-02-04T08:24:55.690 回答