加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

使用多态ADT类型检查的错误Scala代码

发布时间:2020-12-16 08:45:40 所属栏目:安全 来源:网络整理
导读:我正在做一些练习来更好地理解IO monads(在 Functional Programming in Scala之后),并且设法编写了一些错误的代码,这些代码以某种方式通过了编译并让我头疼. 在下面的示例中,我正在编写IO monad的堆栈安全解释器.代码在多态代数数据类型(FlatMap [A??,B])上
我正在做一些练习来更好地理解IO monads(在 Functional Programming in Scala之后),并且设法编写了一些错误的代码,这些代码以某种方式通过了编译并让我头疼.

在下面的示例中,我正在编写IO monad的堆栈安全解释器.代码在多态代数数据类型(FlatMap [A??,B])上进行模式匹配.代码中的错误是k1,然后是k2;这两个函数不能组合,因为k1返回的不同类型(IO [B])比k2期望的(B).代码仍以某种方式进行类型检查,这显然是一个类型检查错误,因为在运行时,自动取消装箱时会出现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,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上进行了测试,行为似乎是相同的:代码编译时没有警告.

解决方法

我认为这是 SI-5195错误的一个案例.如果手动构造嵌套的FlatMap,则无法编写那个而且因为所有类型都是已知的,并且k1和k2显然不可组合.

但是在那种模式匹配类型的io1,k1和k2预先不知道,它们必须被推断,并且我们看到它们被推断错误. […]

编辑
这是另一个尝试解释它如何进行类型检查:如果你自己开始推断k1和k2的类型,你会想出

> k1:X => IO [Y]和k2:Y =>某些X和Y的IO [A]>加上k1和然后k2你需要IO [Y]<:Y 那么是否存在满足这些限制的任何类型Y?是的,这是任何.但是当你应用它时,IO [Y]变为Suspend [Int]而Y只是Int,其中子类型关系不成立.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读