trait IO[F[_], +A]
case class Pure[F[_], +A](get: A) extends IO[F,A]
case class Request[F[_], I, +A](expr: F[I], receive: I => IO[F,A]) extends IO[F,A]
trait Console[A]
case object ReadLine extends Console[Option[String]]
case class PrintLine(s: String) extends Console[Unit]
trait Run[F[_]] {
def apply[A](expr: F[A]): (A, Run[F])
}
object IO {
@annotation.tailrec
def run[F[_],A](R: Run[F])(io: IO[F,A]): A = io match {
case Pure(a) => a
case Request(expr,recv) =>
R(expr) match { case (e,r2) => println(e.getClass); run(r2)(recv(e)) }
}
}
代码来自“ Scala中的功能编程”,IDE抱怨模式匹配中的“ recv”应仅接收Nothing类型作为其参数,而实际上类型为Any。但是,它仍然可以通过编译。我还认为recv将被推断为run [F [?],A]函数的Request [F [?],Nothing,A]。这里发生了什么?看来Scala具有一些动态功能。它可能会推断运行时中的I类型,这是不正确的。
答案 0 :(得分:1)
根据Request
的定义,我们知道expr
的类型为F[I]
,而recv
的类型为I => IO[F, A]
。根据{{1}}的定义,我们知道Run
的类型为R(expr)
。因此(I, Run[F])
的类型为e
,而I
是一个接受类型为recv
的参数的函数。
这告诉我们I
的类型正确,即使我们对recv(e)
一无所知。
我还认为recv将被推断为run [F [?],A]函数的Request [F [?],Nothing,A]。
否,I
的类型为recv
,其中I => IO[F, A]
和F
是A
定义中的类型变量,而run
是其中的一个创建I
对象时,type被用作Request
的第二个类型参数。