为啥 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 编译器会考虑一个类型,而分配给它的值的类型是不同的?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 .NET 的 Scala 编译器会忽略 val 的含义?