functional programming - Erroneous Scala code using polymorphic ADT type checks -
i'm doing exercises better understand io monads (following functional programming in scala), , managed write erroneous code somehow passed compilation , caused me headache.
in below example, writing stack-safe interpreter of io monad. code in pattern matching on polymorphic algebraic data type (flatmap[a, b]). error in code k1 andthen k2; 2 functions can't compose because k1 returning different type (io[b]) k2 expects (b). the code still type-checks somehow, , typechecking error because, in runtime, there classcastexception @ automatic unboxing (just if using unsafe cast in java). there no compiler warnings issued.
the code (also found on gist):
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: => io[b]) extends io[b] object io { def apply[a](a: => a): io[a] = suspend(() => a) } object interpreter { def run[a](io: io[a]): = { io match { case return(a) => case suspend(f) => f() case flatmap(return(a), cont) => run(cont(a)) case flatmap(suspend(f), cont) => run(cont(f())) // case compiles whatever reason shouldn't type check (k1 returns io[b] , k2 expects b) // accordingly, there classcastexception in runtime case flatmap(flatmap(io1, k1), k2) => run(flatmap(io1, k1 andthen k2)) // case 1 works // case flatmap(flatmap(io1, k1), k2) => run(flatten(io1, k1, k2)) } } def flatten[a, b, c](io: io[a], k1: => io[b], k2: b => io[c]): flatmap[a, c] = { flatmap(io, => 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 io loop : $sumn") println(s"sum of 1..$n math expr: ${n * (n - 1) / 2}") assert(sumn == n * (n - 1) / 2) } what going on? compiler bug? or known limitation of type inference? or there explanation this?
i've tested on both scala 2.11.8 , on 2.12.0, , behavior seems same: code compiles without warnings.
i think case of si-5195 bug. if construct nested flatmap manually, cannot write andthen, because types known , k1 , k2 not composable.
but in pattern matching types of io1, k1 , k2 not known in advance, have inferred , see inferred wrong. [...]
edit here try explain how type-checks: if start inferring types k1 , k2 yourself, come with
k1: x => io[y],k2: y => io[a]x,y- plus
k1 andthen k2needio[y] <: y
so there exist type y satisfies these restrictions? yes, it's any. when apply it, io[y] becomes suspend[int] , y int subtype relation doesn't hold.
Comments
Post a Comment