I am trying to use the answer of a preceding question to implement a small graph library. The idea is to consider graphs as colections, where vertices wrap collection elements.
I would like to use abstract types to represent Vertex and Edge types (because of type safety) and I want to use type parameters to represent the type of the collection elements (because I want to define them at instantiation easily).
However, when trying the most basic example I can think about, I am stuck with compile errors. Here is the example:
package graph
abstract class GraphKind[T] {
type V <: Vertex[T]
type G <: Graph[T]
def newGraph(): G
abstract class Graph[T] extends Collection[T]{
self: G =>
def vertices(): List[V]
def add(t: T): Unit
def size(): Int
def elements(): Iterator[T]
}
trait Vertex[T] {
self: V =>
def graph(): G
def value(): T
}
}
And here is the basic implementations:
class SimpleGraphKind[T] extends GraphKind[T] {
type G = GraphImpl[T]
type V = VertexImpl[T]
def newGraph() = new GraphImpl[T]
class GraphImpl[T] extends Graph[T] {
private var vertices_ = List[V]()
def vertices = vertices_
def add( t: T ) { vertices_ ::= new VertexImpl[T](t,this) }
def size() = vertices_.size
def elements() = vertices.map( _.value ).elements
}
class VertexImpl[T](val value: T, val graph: GraphImpl[T]) extends Vertex[T] {
override lazy val toString = "Vertex(" + value.toString + ")"
}
}
When trying to compile, I get:
/prg/ScalaGraph/study/Graph.scala:10: error: illegal inheritance;
self-type GraphKind.this.G does not conform to Collection[T]'s selftype Collection[T]
abstract class Graph[T] extends Collection[T]{
^
/prg/ScalaGraph/study/Graph.scala:33: error: illegal inheritance;
self-type SimpleGraphKind.this.GraphImpl[T] does not conform to SimpleGraphKind.this.Graph[T]'s selftype SimpleGraphKind.this.G
class GraphImpl[T] extends Graph[T] {
^
/prg/ScalaGraph/study/Graph.scala:36: error: type mismatch;
found : SimpleGraphKind.this.VertexImpl[T]
required: SimpleGraphKind.this.V
def add( t: T ) { vertices_ ::= new VertexImpl[T](t,this) }
^
/prg/ScalaGraph/study/Graph.scala:38: error: type mismatch;
found : Iterator[T(in class SimpleGraphKind)]
required: Iterator[T(in class GraphImpl)]
def elements() = vertices.map( _.value ).elements
^
/prg/ScalaGraph/study/Graph.scala:41: error: illegal inheritance;
self-type SimpleGraphKind.this.VertexImpl[T] does not conform to SimpleGraphKind.this.Vertex[T]'s selftype SimpleGraphKind.this.V
class VertexImpl[T](val value: T, val graph: GraphImpl[T]) extends Vertex[T] {
^
5 errors found
I have absolutely no idea of the meaning of these errors... However, if I specialize the type T in the implementation (class SimpleGraphKind extends GraphKind[Int]
I get only the first error.
Do you have some ideas ?
Compiling this with -explaintypes
yields:
<console>:11: error: illegal inheritance;
self-type GraphKind.this.G does not conform to Collection[T]'s selftype Collection[T]
abstract class Graph[T] extends Collection[T]{
^
GraphKind.this.G <: Collection[T]?
Iterable[T] <: Iterable[T]?
T <: T?
T <: Nothing?
<notype> <: Nothing?
false
Any <: Nothing?
<notype> <: Nothing?
false
false
false
Any <: T?
Any <: Nothing?
<notype> <: Nothing?
false
false
false
false
false
GraphKind.this.Graph[T] <: Iterable[T]?
Iterable[T] <: Iterable[T]?
T <: T?
T <: Nothing?
<notype> <: Nothing?
false
Any <: Nothing?
<notype> <: Nothing?
false
false
false
Any <: T?
Any <: Nothing?
<notype> <: Nothing?
false
false
false
false
false
false
false
Now, I was about to write I don't understand how T <: T
can be false -- it is almost like T
was defined twice, which, of course, is the whole problem. Here:
abstract class GraphKind[T] {
type V <: Vertex[T]
type G <: Graph[T]
def newGraph(): G
abstract class Graph[T] extends Collection[T]{
Ok, class GraphKind
is parameterized with T
and type G
must be a Graph[T]
. Now, class Graph
is also parameterized, and its parameter is also called T
. To prevent confusing, let's rewrite it:
abstract class Graph[T2] extends Collection[T2]{
self: G =>
def vertices(): List[V]
def add(t: T2): Unit
def size(): Int
def elements(): Iterator[T2]
}
Note that this is EXACTLY EQUAL to what you wrote. I'm just using a different name for the type parameter, so that it doesn't get confused with the T
that is parameterizing GraphKind
.
So, here is the logic:
G <: Graph[T]
Graph[T2] <: Collection[T2]
Graph[T2] <: G // self type
which implies that
Graph[T2] <: Graph[T]
And, because Graph
extends Collection
:
Collection[T2] <: Collection[T]
But there is no guarantee that this is true. I do not understand why the problem does not show up when the inheritance is not present. Fix:
abstract class GraphKind[T] {
type V <: Vertex
type G <: Graph
def newGraph(): G
abstract class Graph extends Collection[T]{
self: G =>
def vertices(): List[V]
def add(t: T): Unit
def size(): Int
def elements(): Iterator[T]
}
trait Vertex {
self: V =>
def graph(): G
def value(): T
}
}
class SimpleGraphKind[T] extends GraphKind[T] {
type G = GraphImpl
type V = VertexImpl
def newGraph() = new GraphImpl
class GraphImpl extends Graph {
private var vertices_ = List[V]()
def vertices = vertices_
def add( t: T ) { vertices_ ::= new VertexImpl(t,this) }
override def size() = vertices_.size
override def elements() = vertices.map( _.value ).elements
}
class VertexImpl(val value: T, val graph: GraphImpl) extends Vertex {
override lazy val toString = "Vertex(" + value.toString + ")"
}
}
Since Vertex
and Graph
will be tied to one instance of GraphKind
, then T
will be fixed to whatever it was defined for that instance. For example:
scala> new SimpleGraphKind[Int]
res0: SimpleGraphKind[Int] = SimpleGraphKind@1dd0fe7
scala> new res0.GraphImpl
res1: res0.GraphImpl = line10()
scala> res1.add(10)
scala> res1.add("abc")
<console>:9: error: type mismatch;
found : java.lang.String("abc")
required: Int
res1.add("abc")
^
It seems that Vertex's belonging to a particular graph and only that graph can be represented best in the type system with a nested Vertex trait in the Graph. Is what you are trying to achieve met with the following structure?
abstract class Graph[T] extends Collection[T] {
thisGraph: Graph[T] =>
def newGraph: Graph[T]
def vertices: List[Vertex[T]]
def add(t: T): Unit
def size: Int
def elements: Iterator[T]
trait Vertex[T] {
def graph = thisGraph
def value: T
}
}
class SimpleGraph[T] extends Graph[T] {
private[this] var verts = List[Vertex[T]]()
def newGraph = new SimpleGraph[T]
def vertices = verts
def add(t: T) { verts ::= SimpleVertex(t) }
override def size = verts.size
override def elements = verts.map(_.value).elements
def iterator = elements // the "new" elements in 2.8
case class SimpleVertex[T](value: T) extends Vertex[T]
}