我正在尝试实现类型级别的向量。在我尝试实现函数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))
^
这对我来说没有意义,因为v
和that.v
的类型都保证为V
。
我在Scala mill
中使用2.13.0-M5
。
我想念什么?
答案 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
中它是抽象的。
简单地说,您不能调用其参数声明为抽象类型的方法。