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.<
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]
}