Reputation: 296
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)) }
}
}
The code is from "Functional Programming in Scala", the IDE complains that "recv" from the pattern matching should only receive Nothing type as its argument, but actually type is Any. However, it still pass the compile. I also think recv will be inferenced as Request[F[?], Nothing, A] for the run[F[?], A] function. What happened here? It seems like Scala has some dynamic features. It could infer the I type in the runtime which is not true.
Upvotes: 0
Views: 68
Reputation: 370425
From the definition of Request
, we know that expr
has type F[I]
and recv
has type I => IO[F, A]
. From the definition of Run
, we know that R(expr)
will have type (I, Run[F])
. So e
has type I
and recv
is a function that accepts arguments of type I
.
This tells us that recv(e)
is well-typed even though we know nothing else about I
.
I also think recv will be inferenced as Request[F[?], Nothing, A] for the run[F[?], A] function.
No, recv
has type I => IO[F, A]
where F
and A
are the type variables from the definition of run
and I
is whichever type was used as the second type argument to Request
when the Request
object was created.
Upvotes: 1