首页 > 解决方案 > 类型安全向量实现的类型不匹配

问题描述

我正在尝试实现类型级向量。在我尝试实现函数之前add,这一切都很好,这意味着添加两个向量(相同维度)。

这是我到目前为止所拥有的:

object Vector {

  type Vector1[A] = Ex[A, Vector0[A]]

  def of[A](a1: A): Vector1[A] = Ex(Vector0[A](), a1)

  type Vector2[A] = Ex[A, Vector1[A]]

  def of[A](a1: A, a2: A): Vector2[A] = of(a1).ex(a2)

  type Vector3[A] = Ex[A, Vector2[A]]

  def of[A](a1: A, a2: A, a3: A): Vector3[A] = of(a1, a2).ex(a3)

}

trait Vector[A] {

  type Same[B] <: Vector[B]
  type Self <: Vector[A]

  def ex(a: A): Vector[A]

  def add(that: Self): Self

  def map[B](f: A => B): Same[B]

  def forEach(f: A => Unit): Unit
}

case class Vector0[A]() extends Vector[A] {

  type Same[B] = Vector0[B]
  type Self = Vector0[A]

  def ex(that: A): Ex[A, Self] = Ex[A, Self](this, that)

  def add(that: Self): Self = Vector0[A]()

  def map[B](f: A => B): Same[B] = Vector0[B]()

  def forEach(f: A => Unit): Unit = ()
}

case class Ex[A, V <: Vector[A]](v: V, a: A) extends Vector[A] {

  type Same[B] = Ex[B, V#Same[B]]
  type Self = Ex[A, V]

  def ex(that: A): Ex[A, Self] = Ex[A, Self](this, that)

  def add(that: Self)(implicit num: Numeric[A]): Self = Ex[A, V](v.add(that.v), num.plus(a, that.a))

  def map[B](f: A => B): Same[B] = Ex[B, V#Same[B]](v.map(f), f(a))

  def forEach(f: A => Unit): Unit = {
    v.forEach(f)
    f(a)
  }
}

这可能比解决问题所需的代码多,但它可能对任何讨论都有帮助。

现在,看一下addin Ex(“ex”代表“extrude”,例如向向量添加一个维度)。

我收到以下编译错误:

[error]  found   : that.v.type (with underlying type V)
[error]  required: Ex.this.v.Self
[error]   def add(that: Self)(implicit num: Numeric[A]): Self = Ex[A, V](v.add(that.v), num.plus(a, that.a))
                                                                                    ^

这对我来说没有意义,因为两者vthat.v保证是 type V

我正在使用millScala 2.13.0-M5

我错过了什么?

标签: scalatype-level-computation

解决方案


问题是 in 的类型SelfVector抽象的,并且只为 的非抽象子类定义Vector。您可以拥有类型的值,Vector但编译器无法知道该Self值的类型。因此,您不能调用add该值,因为无法检查参数是否是正确的类型(因为类型未知)。

在您的Ex班级中,值v是 typeV <: Vector[A]这意味着它可能是Vector[A]. But addis not defined forVector[A]因为Self没有定义,所以当你尝试调用时编译器会报错v.add

这是一个简化的示例:

 trait A {
   type Self // Abstract type
   def add(a: Self, b: Self): Self
 }

 case class A1() extends A {
   type Self = Int // Concrete type
   def add(a: Int, b: Int) = a + b
 }

 case class A2() extends A {
   type Self = String // Concrete type
   def add(a: String, b: String) = a + b
 }

 val a1 = new A1
 val a2 = new A2

 a1.add(1, 1) // Argument type is Int

 a2.add("A", "B") // Argument type is String

这一切都很好,因为类型是已知的。但是现在创建一个类型的值A并调用add它:

 val a: A = a1
 a.add(1, 1) // fails to compile
 a.add("A", "B") // fails to compile

这不会编译,因为您丢失了类型信息,Self因为A它是抽象的。

简而言之,您不能调用其参数被声明为抽象类型的方法。


推荐阅读