Reputation: 3238
I am trying to implement typelevel vectors. It all worked well until I tried to implement the function add
, which is meant to add two vectors (of same dimensions).
This is what I have so far:
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)
}
}
This is more code than might be necessary for solving the problem, but it might be helpful for any discussion.
Now, take a look at add
in Ex
("ex" stands for "extrude", e.g. adding one more dimension to a vector).
I get the following compile error:
[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))
^
This does not make sense to me, as both v
and that.v
are guaranteed to be of type V
.
I am using mill
with Scala 2.13.0-M5
.
What am I missing?
Upvotes: 0
Views: 83
Reputation: 27356
The problem is that the type Self
in Vector
is abstract and is only defined for a non-abstract subclass of Vector
. You can have values of type Vector
but the compiler cannot know the type of Self
for that value. Therefore you cannot call add
on that value because it is impossible to check that the argument is the right type (because the type is unknown).
In your Ex
class the value v
is type V <: Vector[A]
which means it could be Vector[A]
. But add
is not defined for Vector[A]
because Self
is not defined, so the compiler complains when you try to call v.add
.
Here is a simplified example:
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
This all works fine because the types are known. But now make a value of type A
and call add
on that:
val a: A = a1
a.add(1, 1) // fails to compile
a.add("A", "B") // fails to compile
This does not compile, because you have lost the type information for Self
because in A
it is abstract.
Put simply, you can't call a method whose arguments are declared to be abstract types.
Upvotes: 1