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))
^
问题是,你要价太高了Scala的类型推断的。它试图找出它需要doFoo[?](number)
类型参数,虽然它很清楚地告诉我们作为人类,它已经被赋予F
的背景下doFoo(number)
出现在表达式中,编译器是没有那么聪明。
最简单的办法就是,明确规定了类型参数:
.flatMap(_ => doFoo[F](number))
如果您发现恼人的,你可以通过脱糖的F[_]: Monad
约束的约束,这样就可以使Console
和Monad
实例明确的顺序帮助编译了一下:
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)
电话。