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

了解Scala GADT支持的限制

发布时间:2020-12-16 09:21:30 所属栏目:安全 来源:网络整理
导读:Test.test中的错误似乎是不合理的: sealed trait A[-K,+V]case class B[+V]() extends A[Option[Unit],V]case class Test[U]() { def test[V](t: A[Option[U],V]) = t match { case B() = null // constructor cannot be instantiated to expected type; fo
Test.test中的错误似乎是不合理的:

sealed trait A[-K,+V]
case class B[+V]() extends A[Option[Unit],V]

case class Test[U]() { 
  def test[V](t: A[Option[U],V]) = t match {
    case B() => null // constructor cannot be instantiated to expected type; found : B[V] required: A[Option[U],?V1] where type ?V1 <: V (this is a GADT skolem)
  }
  def test2[V](t: A[Option[U],V]) = Test2.test2(t)
}

object Test2 {
  def test2[U,V](t: A[Option[U],V]) = t match {
    case B() => null // This works
  }
}

有几种方法可以使错误更改或消失:

如果我们删除trait A(和case B类)上的V参数,错误的“GADT-Skolem”部分就会消失,但是“构造函数不能被实例化”的部分仍然存在.

如果我们将Test类的U参数移动到Test.test方法,则该错误消失.为什么(类似地,Test2.test2中不存在该错误)

以下链接也标识出这个问题,但我不明白提供的解释. http://lambdalog.seanseefried.com/tags/GADTs.html

这是编译器中的错误吗? (2.10.2-RC2)

感谢您的任何帮助澄清.

2014/08/05:我已经设法进一步简化代码,并提供另一个例子,其中U绑定在即时函数之外,而不会导致编译错误.我仍然在2.11.2中看到这个错误.

sealed trait A[U]
case class B() extends A[Unit]

case class Test[U]() {
  def test(t: A[U]) = t match {
    case B() => ??? // constructor cannot be instantiated to expected type; found : B required: A[U]
  }
}

object Test2 {
  def test2[U](t: A[U]) = t match {
    case B() => ??? // This works
  }
  def test3[U] = {
    def test(t: A[U]) = t match {
      case B() => ??? // This works
    }
  }
}

像这样简化,这看起来更像编译器的bug或限制.还是我错过了什么?

解决方法

构造函数模式必须符合模式的预期类型,这意味着B <:A [U],如果U是当前正在调用的方法的类型参数,则为真(因为可以将其实例化为适当类型参数),但如果U是先前绑定的类类型参数,则不真实. 你当然可以质疑“必须符合”规则的价值.我知道我有我们通常通过上传监护程序来避免这个错误,直到构造函数模式符合.特别,

// Instead of this
def test1(t: A[U]) = t match { case B() => ??? }

// Write this
def test2(t: A[U]) = (t: A[_]) match { case B() => ??? }

附录:在评论中,提问者说:“问题很简单,为什么它不适用于类级别的类型参数,但在方法级别工作.实例化类类型参数在外部可见,并且具有无限期的生命周期,对于实例化的方法类型参数都不是这样.这对类型健全有影响.给予Foo [A],一旦我拥有一个Foo [Int],那么当指向该实例时,A必须是Int,永远和永远.

原则上,您可以在构造函数调用中类似地处理类类型参数,因为type参数仍然是未绑定的,并且可以机会地推断.就是这样.一旦出现在世界各地,就没有重新谈判的余地.

另外一个补充:我看到人们做了很多工作,把编译器作为正确性的一个前提,我们所需要做的只是思考它的输出,直到我们的理解发展到足以匹配它为止.这样的精神体操已经在那个帐篷下发生了,我们可以帮助太阳马戏团过几次.

scala> case class Foo[A](f: A => A)
defined class Foo

scala> def fail(foo: Any,x: Any) = foo match { case Foo(f) => f(x) }
fail: (foo: Any,x: Any)Any

scala> fail(Foo[String](x => x),5)
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
  at $anonfun$1.apply(<console>:15)
  at .fail(<console>:13)
  ... 33 elided

这是scala的当前版本 – 这仍然是它所做的.没有警告.所以也许问自己,在十多年的生存之后,提出对一种语言和实施的正确性推定是非常明智的.

(编辑:李大同)

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

    推荐文章
      热点阅读