Explicitly Typed Self References

When developing extensible software it is sometimes handy to declare the type of the value `this` explicitly. To motivate this, we will derive a small extensible representation of a graph data structure in Scala.

Here is a definition describing graphs:

``````abstract class Graph {
type Edge
type Node <: NodeIntf
abstract class NodeIntf {
def connectWith(node: Node): Edge
}
def nodes: List[Node]
def edges: List[Edge]
}
``````

Graphs consist of a list of nodes and edges where both the node and the edge type are left abstract. The use of abstract types allows implementations of trait Graph to provide their own concrete classes for nodes and edges. Furthermore, there is a method `addNode` for adding new nodes to a graph. Nodes are connected using method `connectWith`.

A possible implementation of class `Graph` is given in the next class:

``````abstract class DirectedGraph extends Graph {
type Edge <: EdgeImpl
class EdgeImpl(origin: Node, dest: Node) {
def from = origin
def to = dest
}
class NodeImpl extends NodeIntf {
def connectWith(node: Node): Edge = {
val edge = newEdge(this, node)
edges = edge :: edges
edge
}
}
protected def newNode: Node
protected def newEdge(from: Node, to: Node): Edge
var nodes: List[Node] = Nil
var edges: List[Edge] = Nil
val node = newNode
nodes = node :: nodes
node
}
}
``````

Class `DirectedGraph` specializes the `Graph` class by providing a partial implementation. The implementation is only partial, because we would like to be able to extend `DirectedGraph` further. Therefore this class leaves all implementation details open and thus both the edge and the node type are left abstract. Nevertheless, class `DirectedGraph` reveals some additional details about the implementation of the edge type by tightening the bound to class `EdgeImpl`. Furthermore, we have some preliminary implementations of edges and nodes represented by the classes `EdgeImpl` and `NodeImpl`. Since it is necessary to create new node and edge objects within our partial graph implementation, we also have to add the factory methods `newNode` and `newEdge`. The methods `addNode` and `connectWith` are both defined in terms of these factory methods. A closer look at the implementation of method `connectWith` reveals that for creating an edge, we have to pass the self reference `this` to the factory method `newEdge`. But `this` is assigned the type `NodeImpl`, so it’s not compatible with type `Node` which is required by the corresponding factory method. As a consequence, the program above is not well-formed and the Scala compiler will issue an error message.

In Scala it is possible to tie a class to another type (which will be implemented in future) by giving self reference `this` the other type explicitly. We can use this mechanism for fixing our code above. The explicit self type is specified within the body of the class `DirectedGraph`.

Here is the fixed program:

``````abstract class DirectedGraph extends Graph {
type Edge <: EdgeImpl
class EdgeImpl(origin: Node, dest: Node) {
def from = origin
def to = dest
}
class NodeImpl extends NodeIntf {
self: Node =>                     // new line
def connectWith(node: Node): Edge = {
val edge = newEdge(this, node)  // now legal
edges = edge :: edges
edge
}
}
protected def newNode: Node
protected def newEdge(from: Node, to: Node): Edge
var nodes: List[Node] = Nil
var edges: List[Edge] = Nil
val node = newNode
nodes = node :: nodes
node
}
}
``````

In this new definition of class `NodeImpl`, `this` has type `Node`. Since type `Node` is abstract and we therefore don’t know yet if `NodeImpl` is really a subtype of `Node`, the type system of Scala will not allow us to instantiate this class. But nevertheless, we state with the explicit type annotation of this that at some point, (a subclass of) `NodeImpl` has to denote a subtype of type `Node` in order to be instantiatable.

Here is a concrete specialization of `DirectedGraph` where all abstract class members are turned into concrete ones:

``````class ConcreteDirectedGraph extends DirectedGraph {
type Edge = EdgeImpl
type Node = NodeImpl
protected def newNode: Node = new NodeImpl
protected def newEdge(f: Node, t: Node): Edge =
new EdgeImpl(f, t)
}
``````

Please note that in this class, we can instantiate `NodeImpl` because now we know that `NodeImpl` denotes a subtype of type `Node` (which is simply an alias for `NodeImpl`).

Here is a usage example of class `ConcreteDirectedGraph`:

``````object GraphTest extends App {
val g: Graph = new ConcreteDirectedGraph