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

scala的类型检查器无法识别抽象路径依赖类场景中的类型

发布时间:2020-12-16 18:31:39 所属栏目:安全 来源:网络整理
导读:让我们用抽象类定义一个特征 object Outer { trait X { type T val empty: T } 现在我们可以创建它的一个实例: val x = new X { type T = Int val empty = 42 } Scala现在认识到,x.empty是一个Int: def xEmptyIsInt = x.empty: Int 现在,让我们定义另一个
让我们用抽象类定义一个特征

object Outer {

  trait X {
    type T
    val empty: T
  }

现在我们可以创建它的一个实例:

val x = new X {
    type T = Int
    val empty = 42
  }

Scala现在认识到,x.empty是一个Int:

def xEmptyIsInt = x.empty: Int

现在,让我们定义另一个类

case class Y(x: X) extends X {
    type T = x.T
    val empty = x.empty
  }

并制作一个实例

val y = Y(x)

但是现在Scala无法推断y.empty属于Int类型.下列

def yEmptyIsInt = y.empty: Int

现在生成错误消息:

error: type mismatch;
found   : y.x.T
required: Int
       y.empty : Int

为什么会这样?这是一个scala bug吗?

我们可以通过向case类引入参数来缓解此问题:

case class Z[U](x: X { type T = U }) extends X {
    type T = U
    val empty = x.empty
  }

然后再次运作

val z = Z(x)
  def zEmptyIsInt: Int = z.empty

但我们总是要在呼叫现场提到X内的所有类型.理想情况下,这应该是一个实现细节,导致以下方法:

case class A[U <: X](z: U) extends X {
    type T = z.T
    val empty = z.empty
  }

这也减轻了这个问题

val a = A(x)
  def aEmptyIsInt: Int = a.empty

}

总而言之,我的问题如下:为什么简单的情况不起作用?这是一个有效的解决方法吗?当我们遵循两种解决方法之一时,可能会出现哪些其他问题?有更好的方法吗?

解决方法

你已经为不同的东西重新使用了x,所以从这里开始我将调用val x“instance x”实例化的对象和类Y“parameter x”中使用的x:X.

“实例x”是特征X的anonymous subclass,具体成员覆盖特征X的抽象成员.作为X的子类,您可以将它传递给case类Y的构造函数,并且它将被愉快地接受,因为作为子类,它是X.

在我看来,你希望案例类Y将在运行时检查它是否传递的X实例是否已覆盖X的成员,并生成Y的实例,其成员具有不同的类型,具体取决于传入的内容.

这显然不是Scala如何工作,并且几乎会破坏其(静态)类型系统的目的.例如,如果没有运行时反射,你就无法对Y.empty做任何有用的事情,因为它可以有任何类型,那时你最好只使用动态类型系统.如果你想要参数,自由定理,不需要反射等的好处,那么你必须坚持静态确定的类型(模式匹配的一个小例外).这就是Scala在这里所做的.

实际发生的是你告诉Y的构造函数,参数x是一个X,因此编译器静态地确定x.empty的类型为X.empty,它是抽象类型T. Scala的推断没有失败;你的类型实际上是不匹配的.

另外,这与路径依赖类型没有任何关系.这是一个很好的walkthrough,但简而言之,路径依赖类型绑定到它们的父实例,而不是在运行时动态确定.

(编辑:李大同)

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

    推荐文章
      热点阅读