I'm adding type annotations to this pattern match just for my own understanding.
@annotation.tailrec def run[A](io: IO[A]): A = {
io match {
case Return(a) => a
case Suspend(r) => r()
case FlatMap(x, f) => x match {
case Return(a) => run(f(a))
case Suspend(r) => run(f(r()))
case FlatMap(y, g) =>
run(y flatMap (a => g(a) flatMap f))
}
}
}
Why do these type annotations break the tail recursion check? With the new type definitions and type annotations added, I don't clearly see a costly new recursion.
could not optimize @tailrec annotated method run: it contains a recursive call not in tail position
io match {
^
@annotation.tailrec def run[A](io: IO[A]): A = {
type rType = Unit => A
type fType = A => IO[A]
type gType = A => IO[A]
io match {
case Return(a: A) => a
case Suspend(r: rType) => r()
case FlatMap(x: IO[A], f: fType) => x match {
case Return(a: A) => run(f(a))
case Suspend(r: rType) => run(f(r()))
case FlatMap(y: IO[A], g: gType) =>
run(y flatMap (a => g(a) flatMap f))
}
}
}
The case classes being matched against:
case class Return[A](a: A) extends IO[A]
case class Suspend[A](resume: () => A) extends IO[A]
case class FlatMap[A,B](sub: IO[A], k: A => IO[B]) extends IO[B]
As long as the type annotations are omitted, type of 'a' in line
F.flatMap(r)((a: A) => run(f(a)))
must be 'Any':
[error] found : A => F[A]
[error] required: Any => F[A]
[error] F.flatMap(r)((a: A) => run(f(a)))
This compiles:
F.flatMap(r)(a => run(f(a)))
Bonus question.
It seems that pattern matching against a function inside a case class, like this, is not allowed:
io match {
...
case Suspend(r: Unit => A) => r()
/* or */
case Suspend(r: () => A) => r()
...
}
This compiles:
io match {
...
case Suspend(r: Function0[A]) => r()
...
}
Why is this?
These type annotations won't have much use in the end because of type erasure. After annotating these types, I can expect to see a compiler warning like this:
abstract type pattern ... is unchecked since it is eliminated by erasure
This code is from Chapter 13, or package fpinscala.iomonad, of "Functional Programming in Scala." https://github.com/fpinscala/fpinscala
Thanks
Bonus answer: There are a lot of questions about type erasure, take a look at How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections? You can write something like