Circular type parameters definition in scala
-
20-09-2019 - |
Question
I am trying to define a generic container whose elements can return the enclosing container. Something like:
abstract class Container[E <: Element] { // compile error
def contains( e: E ): Boolean
def addNewElement(): Unit
}
abstract class Element[C <: Container] { // compile error
def enclosingContainer(): C
}
class MyContainer extends Container[MyElement] {
private var elements = List[MyElement]()
override def contains( elem: MyElement ) = elements.contains( elem )
override def addNewElement() { elements ::= new MyElement(this) }
}
class MyElement( container: MyContainer ) extends Element[MyContainer] {
override val enclosingContainer = container
}
However that snippet does not compile because I should give a type parameter to Element
in the abstract class Container[E <: Element]
definition and a type to Container
in the abstract class Element[C <: Container]
definition.
I there a way to achieve the behavior I am looking for ? Is there an appropriate declaration for Container
and Element
? Should I define a third-party object ?
Solution
The other solutions already given fail to enforce that the types match: that is, given a type ContainerImpl extends Container
, you should be sure that ContainerImpl.E.C
should be ContainerImpl
and not some other container. Here is one which does enforce this (adapted from http://programming-scala.labs.oreilly.com/ch13.html):
abstract class ContainerWithElement {
type C <: Container
type E <: Element
trait Container {
self: C =>
def contains( e: E ): Boolean
def addNewElement(): Unit
}
trait Element {
self: E =>
def enclosingContainer(): C
}
}
OTHER TIPS
abstract class Container[E <: Element[_]] {
def contains( e: E ): Boolean
def addNewElement(): Unit
}
abstract class Element[C <: Container[_]] {
def enclosingContainer(): C
}
class MyContainer extends Container[MyElement] {
private var elements = List[MyElement]()
override def contains( elem: MyElement ) = elements.contains( elem )
override def addNewElement() { elements ::= new MyElement(this) }
}
class MyElement( container: MyContainer ) extends Element[MyContainer] {
override val enclosingContainer = container
}
Using type members instead of type parameters would avoid the issue:
abstract class Container { // compile error
type E <: Element
def contains( e: E ): Boolean
def addNewElement(): Unit
}
abstract class Element { // compile error
type C <: Container
def enclosingContainer(): C
}
class MyContainer extends Container {
type E = MyElement
private var elements = List[MyElement]()
override def contains( elem: MyElement ) = elements.contains( elem )
override def addNewElement() { elements ::= new MyElement(this) }
}
class MyElement( container: MyContainer ) extends Element {
type C = MyContainer
override val enclosingContainer = container
}