为啥 Scala 编译器会考虑一个类型,而分配给它的值的类型是不同的?

Posted

技术标签:

【中文标题】为啥 Scala 编译器会考虑一个类型,而分配给它的值的类型是不同的?【英文标题】:Why would the Scala compiler consider a type, and the type of a value assigned it to be different?为什么 Scala 编译器会考虑一个类型,而分配给它的值的类型是不同的? 【发布时间】:2021-02-24 01:50:49 【问题描述】:

我有一个表示向量的简单类,这样不同长度的向量可以解析为不同的类型。但是,我在实施 zip 方法时遇到了一些困难。这是我所做工作的最小化版本,以及我觉得有点神秘的编译器消息。

  sealed trait Vector[A] 

    type ThisButType[B] <: Vector[B]

    def zip[B](other: ThisButType[B]): ThisButType[Tuple2[A, B]]
  

  case class Term[A]() extends Vector[A] 
   
    type ThisButType[B] = Term[B]
   
    def zip[B](other: ThisButType[B]): ThisButType[Tuple2[A, B]] = Term[Tuple2[A, B]]()
  
  

  case class Component[A, T <: Vector[A]](value: A, tail: T) extends Vector[A]

    type ThisButType[B] = Component[B, T#ThisButType[B]]

    def zip[B](other: ThisButType[B]): ThisButType[Tuple2[A, B]] =
      Component[Tuple2[A,B], T#ThisButType[Tuple2[A, B]]]((value, other.value), tail.zip(other.tail))
  

我得到的错误是:

 type mismatch;
   found   : other.tail.type (with underlying type T#ThisButType[B])
   required: Component.this.tail.ThisButType[B]

为什么编译器无法确定在这种情况下 T 应该与 Component.this.tail 相同?

【问题讨论】:

【参考方案1】:

假设是这样:

// applying some other minor changes...
sealed trait Vector[A] 
    type ThisButType[B] <: Vector[B]
    def zip[B](other: ThisButType[B]): ThisButType[(A, B)]

case class Term[A]() extends Vector[A] 
    override type ThisButType[B] = Term[B]
    override def zip[B](other: Term[B]): Term[(A, B)] = Term()

case class Component[A, T <: Vector[A]](value: A, tail: T) extends Vector[A] 
    override type ThisButType[B] = Component[B, T#ThisButType[B]]
    override def zip[B](other: Component[B, T#ThisButType[B]]): Component[(A, B), T#ThisButType[(A, B)]] =
        Component((value, other.value), tail.zip(other.tail.asInstanceOf[tail.ThisButType[B]]))

然后……

val zero = Term[Unit]()
val zero0: Vector[Unit]#ThisButType[Int] = Term[Int](): zero.ThisButType[Int] // the mystical double type ascription!
val two = Component((), Component((), Term[Unit]()): Vector[Unit])
two.zip(Component(0, zero0)) // ClassCastException!

因此,您的代码确实存在类型错误,编译器将其视为错误而拒绝是正确的。具体来说,您的代码中的T#ThisButType[B]tail.ThisButType[B] 完全相同,因为tail 不必是精确类型T,但可以是某个子类型。在这种情况下,T#ThisButType[B] 可以包含 tail.ThisButType[B] 不包含的值,这意味着您不能将前者 (other.tail) 传递给需要后者的函数 (tail.zip)。

故事的寓意:类型投影是邪恶的,应该避免。你有一个非常好的价值 tail: T 就在那里;只是从那个项目。

// applying some more minor changes...
sealed trait Vector[+A] 
    type ThisButType[+B] <: Vector[B]
    def zip[B](other: ThisButType[B]): ThisButType[(A, B)]

case object Term extends Vector[Nothing] 
    override type ThisButType[+B] = Term.type
    override def zip[B](other: Term.type): Term.type = Term

case class Component[+A, +T <: Vector[A]](value: A, tail: T) extends Vector[A] 
    override type ThisButType[+B] = Component[B, tail.ThisButType[B]]
    override def zip[B](other: Component[B, tail.ThisButType[B]]): Component[(A, B), tail.ThisButType[(A, B)]] =
        Component((value, other.value), tail.zip(other.tail))

之前的例子不再有效:

val zero: Vector[Unit]#ThisButType[Int] = Term: Term.ThisButType[Int]
val two = Component((), Component((), Term): Vector[Unit])
two.zip(Component(0, zero)) // fails: don't know that zero: two.tail.ThisButType[Int]

现在代码是正确的。

【讨论】:

谢谢,我也尝试过使用tail,但会导致不同但相似的问题,即zip 不会接受Component[X, Component[X, Term]] 类型的值。我开始认为这整个想法是不可能的,而且你不能有类型向量。 ?也许这值得一个单独的问题,但对我来说,至少像Component(1, Component(2, Term)).zip(Component('1', Component('2', Term))) 这样的基本东西是有效的。您可能需要在 Vector 中添加额外的“证明”以获得更高级的内容,例如 def idemThisButType[A, B]: ThisButType[A]#ThisButType[B] =:= ThisButType[B](我认为这是真的......) @Lucas 您的zip 不是实际的zip,它会将向量切割成最短的向量。您的 zip 仅针对相同长度的向量进行编译。 @DmytroMitin 如果需要截断 zip,那么我会尝试使用类型级别的自然方法而不是这种方法。 @HTNW 是的,它适用于值,但会导致依赖于 zip 的函数出现很多问题。我不知道我可以告诉编译器这样的事情,听起来这可能就是我要找的。​​span>

以上是关于为啥 Scala 编译器会考虑一个类型,而分配给它的值的类型是不同的?的主要内容,如果未能解决你的问题,请参考以下文章

为啥重载的scala函数需要返回类型?

为啥 .NET 的 Scala 编译器会忽略 val 的含义?

当 Row 接受可变参数时,为啥 Scala 编译器会失败并显示“此处不允许 ': _*' 注释”?

FORTRAN为啥要将数据区分类型

为啥方差注释会导致 Scala 无法推断出这种子类型关系?

为啥 OCaml 中的模块类型注释会导致此代码无法编译?