IO 모나드 (Functional Programming in Scala)를 더 잘 이해하기 위해 몇 가지 연습을하고 있는데, 어떻게 든 컴파일을 통과하고 나에게 두통을 일으키는 오류 코드를 작성했습니다. 아래의 예에서 다형성 ADT 유형 검사를 사용하는 잘못된 스칼라 코드
는, 나는 IO 모나드의 스택 안전 통역을 쓰고 있어요. 이 코드는 다형성 대수 데이터 형식 (FlatMap[A, B]
)의 패턴 일치에 있습니다. 코드의 오류는
k1 andThen k2
입니다.
k1
이
k2
이 기대하는 것 (
B
)보다 다른 유형 (
IO[B]
)을 반환하기 때문에 두 함수를 작성할 수 없습니다.
코드는 여전히 어떤 식 으로든을 유형 검사합니다. 런타임시 Java에서 안전하지 않은 캐스트를 사용하는 것처럼 자동 언 박싱시
ClassCastException
이 있기 때문에 분명히 typechecking 오류입니다. 또한 컴파일러 경고가 발행되지 않습니다.
(또한 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에서 테스트를했는데 동작이 동일하게 보입니다. 코드가 경고없이 컴파일됩니다.
실제로 그렇게 보입니다! 이 방향에 대한 연구가 끝난 후에도 다음과 같이 발견했습니다. 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