Created
November 11, 2016 08:07
-
-
Save zsolt-donca/b1c5ec97e265ff691f962efe0792235b to your computer and use it in GitHub Desktop.
Why does this Scala code type check even though it shouldn't?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object IOMonadExercise extends App { | |
sealed trait IO[A] | |
case class Return[A](value: A) extends IO[A] | |
case class Suspend[A](f: () => A) extends IO[A] | |
case class FlatMap[A, B](io: IO[A], cont: A => IO[B]) extends IO[B] | |
object IO { | |
def apply[A](a: => A): IO[A] = Suspend(() => a) | |
} | |
object Interpreter { | |
def run[A](io: IO[A]): A = { | |
io match { | |
case Return(a) => a | |
case Suspend(f) => f() | |
case FlatMap(Return(a), cont) => run(cont(a)) | |
case FlatMap(Suspend(f), cont) => run(cont(f())) | |
// this case compiles for whatever reason but shouldn't type check (k1 returns IO[B] and k2 expects just B) | |
// accordingly, there is a ClassCastException in the runtime | |
case FlatMap(FlatMap(io1, k1), k2) => run(FlatMap(io1, k1 andThen k2)) | |
// this case is the one that actually works | |
// case FlatMap(FlatMap(io1, k1), k2) => run(flatten(io1, k1, k2)) | |
} | |
} | |
def flatten[A, B, C](io: IO[A], k1: A => IO[B], k2: B => IO[C]): FlatMap[A, C] = { | |
FlatMap(io, a => FlatMap(k1(a), k2)) | |
} | |
} | |
def sum(i: Int): IO[Int] = { | |
Stream.range(0, i).foldLeft(IO(0))((io, i) => FlatMap(io, (s: Int) => IO(s + i))) | |
} | |
val n = 100000 | |
val sumNIO: IO[Int] = sum(n) | |
val sumN: Int = Interpreter.run(sumNIO) | |
println(s"sum of 1..$n by IO loop : $sumN") | |
println(s"sum of 1..$n by math expr: ${n * (n - 1) / 2}") | |
assert(sumN == n * (n - 1) / 2) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment