4

IOモナド(Functional Programming in Scalaの後)をよく理解するためにいくつかの練習をしていて、何とかコンパイルをパスして頭痛を引き起こしたいくつかのエラーコードを書きました。多型ADT型チェックを使用している間違ったScalaコード

以下の例では、私はIOモナドのスタックセーフインタープリタを書いています。コードは、多形代数データ型(FlatMap[A, B])のパターンマッチングにあります。コード内のエラーはk1 andThen k2です。​​がk2が期待するもの(B)よりも異なるタイプ(IO[B])を返すため、2つの関数を作成できません。 コードはまだ何らかの形でを型チェックしていますが、実行時には自動的にunboxingにClassCastExceptionがあるため(Javaで安全でないキャストを使用しているかのように)、型検査エラーです。コンパイラの警告も発行されていません。

(も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: 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) 
} 

が起こっていますか?これはコンパイラのバグですか?または、これは型推論の既知の制限ですか?それともこれについての説明がありますか?

私はScala 2.11.8と2.12.0の両方でテストしましたが、動作は同じようです:コードは警告なしでコンパイルされます。

答えて

1

これはSI-5195バグのケースだと思います。ネストされたFlatMapを手動で構築すると、すべてのタイプが既知であり、​​とk2は明らかに構成できないため、andThenと書くことはできません。

しかし、io1,​​およびk2のパターンマッチングタイプは、事前には分かっていませんので、推測する必要があります。われわれは間違って推測しています。 [...]

EDITここ は、別のは、それがチェックを入力する方法を説明しようとしている:あなたは​​とk2を自分で種類を推定起動した場合、あなたは

  • k1: X => IO[Y]k2: Y => IO[A]を考え出すだろうk1 andThen k2からいくつかのXY
  • プラスのためにあなたが必要IO[Y] <: Y

したがって、これらの制限を満たすタイプYが存在しますか?はい、それはAnyです。 これを適用すると、Suspend[Int]になり、Yはサブタイプの関係が成立しないIntになります。

+1

本当にそうですね!この方向でいくつかの研究をした後、私はこれも見つけました:http://stackoverflow.com/questions/20359696/scala-pattern-match-infers-any-instead-of-an-existential-type-breaks-type- saf また、この問題はSI-5195に関連しているようです:https://issues.scala-lang.org/browse/SI-6680 – dzs

関連する問題