[我无法用更少的冗长来解释这个问题。问题的核心是编译器推断出下划线(_
)类型。特别是[_ >: SomeType <: SomeOtherType]
。我不知道何时,如何以及为什么这样]
作为scala练习,我正在尝试对给定大小的元素向量进行编码。
作为必要的步骤,我首先复制了现有的自然数编码:
sealed trait Natural extends Product with Serializable {
def +(that: Natural): Natural
}
case object Zero extends Natural {
override def +(that: Natural): Natural = that
}
final case class Suc[N <: Natural](n: N) extends Natural {
override def +(that: Natural): Natural = Suc(n + that)
}
我相信下图是对类型关系的忠实描绘:
然后,我尝试对由元素上的类型和大小上的另一种类型参数化的向量建模。不过,为了解释这个问题,我假定了一个ints向量,并且仅参数化了该向量的大小:
import shapeless.=:!=
sealed trait Vector[+Size <: Natural] extends Product with Serializable {
def head: Int
def tail: Vector[Natural]
def plus[OtherSize >: Size <: Natural]
(that: Vector[OtherSize])
(implicit ev: OtherSize =:!= Natural): Vector[OtherSize]
}
case object VectorNil extends Vector[Zero.type] {
override def head: Nothing = throw new Exception("Boom!")
override def tail: Vector[Zero.type] = throw new Exception("Boom")
override def plus[OtherSize >: Zero.type <: Natural]
(that: Vector[OtherSize])
(implicit ev: =:!=[OtherSize, Natural]): Vector[OtherSize] = this
}
final case class VectorCons[N <: Natural](
head: Int,
tail: Vector[N]
) extends Vector[Suc[N]] {
override def plus[OtherSize >: Suc[N] <: Natural]
(that: Vector[OtherSize])
(implicit ev: =:!=[OtherSize, Natural]): Vector[OtherSize] = that
}
请注意,VectorCons[N]
实际上是大小为Suc[N]
的向量。 (扩展Vector[Suc[N]]
)。
方法plus
应该添加两个相同大小的向量的元素。我想将类型总和相同的向量求和提高到类型级别。
请注意,类型边界OtherSize >: Size <: Natural
与隐式证据的结合应该可以实现这一点(请参见底部的类似示例),但是:
val foo = VectorCons(1, VectorCons(2, VectorNil))
//type -> VectorCons[Suc[Zero.type]]
// note that foo is (can be viewed) as Vector[Suc[Suc[Zero.type]], i.e
// a vector of 2 elements
val bar = VectorCons(3, VectorNil)
//type -> VectorCons[Zero.type]
val baz = foo.plus(bar)
//type -> Vector[Suc[_ >: Suc[Zero.type] with Zero.type <: Natural]] !! How is this possible ?? !!
让我感到沮丧的是,baz
编译得很好!无形的类型约束无效。好吧,因为OtherSize
确实与Natural
不同;特别是Suc[_ >: Suc[Zero.type] with Zero.type <: Natural]
。
因此,我对baz
的类型非常困惑!这就是可以绕开约束的原因。
baz
/ bar
类型推断出什么?在这一点上,我不关心这是否是向量的正确编码。只是想了解编译器如何为baz
推断该类型?
p.s
1-我知道在VectorCons上实现方法that
返回的plus
并没有达到plus
语义所暗示的含义,但这对问题并不重要。
######额外#######
我怀疑这种奇怪的行为(至少对我而言)与自然数有关。下面的代码工作正常! :
[忽略代码的荒谬语义]
sealed trait Animal extends Product with Serializable
case class Dog() extends Animal
case class Cow() extends Animal
case object NoBox extends Animal //Not important
和
trait Vector[+A <: Animal] {
def head: A
def plus[That >: A <: Animal]
(that: Vector[That])
(implicit ev: =:!=[That, Animal]): Vector[That]
}
case object Nil extends Vector[NoBox.type] {
def head: Nothing = throw new NoSuchElementException("Boom!")
override def plus[That >: NoBox.type <: Animal]
(that: Vector[That])(implicit ev: =:!=[That, Animal]): Vector[That] = this
}
case class Cons[A <: Animal](head: A) extends Vector[A] {
override def plus[That >: A <: Animal]
(that: Vector[That])(implicit ev: =:!=[That, Animal]): Vector[That] = that
}
其中:
val foo = Cons(Dog())
val bar = Cons(Cow())
// Compiles
val baz = foo.plus(foo)
val baz2 = bar.plus(bar)
// Does not compile (what I would expect)
val baz3 = bar.plus(foo)
val baz4 = foo.plus(bar)
感谢您的输入,