chenyuandong
chenyuandong

Reputation: 296

Scala type inference confusion

  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

Answers (1)

sepp2k
sepp2k

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

Related Questions