4

IO 모나드 (Functional Programming in Scala)를 더 잘 이해하기 위해 몇 가지 연습을하고 있는데, 어떻게 든 컴파일을 통과하고 나에게 두통을 일으키는 오류 코드를 작성했습니다. 아래의 예에서 다형성 ADT 유형 검사를 사용하는 잘못된 스칼라 코드

는, 나는 IO 모나드의 스택 안전 통역을 쓰고 있어요. 이 코드는 다형성 대수 데이터 형식 ( FlatMap[A, B])의 패턴 일치에 있습니다. 코드의 오류는 k1 andThen k2입니다. k1k2이 기대하는 것 ( 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에서 테스트를했는데 동작이 동일하게 보입니다. 코드가 경고없이 컴파일됩니다.

답변

1

이것은 SI-5195 버그의 사례라고 생각합니다. 중첩 된 FlatMap을 수동으로 생성하는 경우 모든 유형이 알려져 있고 k1k2은 분명히 구성 할 수 없기 때문에 andThen을 쓸 수 없습니다.

그러나 io1, k1k2의 유형과 일치하는 패턴을 사전에 알 수없는에서

, 그들은 추론해야하고 우리가 보는대로 잘못 추정된다. [...]

여기에 편집 다른이 입력-검사를하는 방법을 설명하려고이다, 당신은

  • k1: X => IO[Y]k2: Y => IO[A]와 함께 올 것이다 당신이 k1k2 자신을 추론 유형을 시작하는 경우 k1 andThen k2 일부 XY
  • 플러스 당신이 필요 IO[Y] <: Y

이러한 제한 사항을 충족시키는 유형 Y이 있습니까? 예, Any입니다. 그러나 적용 할 때 IO[Y]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