类型安全向量实现的类型不匹配

时间:2018-11-04 19:12:31

标签: scala type-level-computation

我正在尝试实现类型级别的向量。在我尝试实现函数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)
  }
}

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

现在,看看add中的Ex(“ ex”代表“拉伸”,例如,向向量增加一个维)。

我收到以下编译错误:

[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的类型都保证为V

我在Scala mill中使用2.13.0-M5

我想念什么?

1 个答案:

答案 0 :(得分:1)

问题在于Self中的类型Vector是抽象的,仅为Vector的非抽象子类定义。您可以具有类型Vector的值,但是编译器无法知道该值的Self的类型。因此,您无法在该值上调用add,因为无法检查参数是否为正确的类型(因为类型未知)。

在您的Ex类中,值v的类型为V <: Vector[A],这意味着它可以为Vector[A]。但是未为add定义Vector[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中它是抽象的。

简单地说,您不能调用其参数声明为抽象类型的方法。