Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala recursive generics: Parent[Child] and Child[Parent]

Update: Clarified and expanded, since the original question was simplified too far

I need a pair of traits, each of which refers to the other such that parent and child classes must relate to each other.

trait Parent [C <: Child] {
  def foo(c: C)
}

trait Child [P <: Parent] {
  def parent: P = ...
  def bar = parent.foo(this)
}

Such that implementing classes must come in pairs:

class ActualParent extends Parent [ActualChild] {
  def foo(c: ActualChild) = ...
}

class ActualChild extends Child [ActualParent] {
}

Unfortunately, the compiler doesn't like these traits because the generic types aren't complete. Instead of C <: Child it needs to say C <: Child[something]. Leaving them unspecified doesn't work either:

trait Parent [C <: Child[_]] {
  def foo(c: C)
}

trait Child [P <: Parent[_]] {
  def parent: P = ...
  def bar = parent.foo(this)
}

It now complains on the parent.foo(this) line, because it doesn't know that this is of the correct type. The type of parent needs to be Parent[this.type] for the call to foo to have the right types.

I'm thinking there must be a way of referring to an object's own type? Or to a type that's required to be itself?


Update: Further to @Daniel's answer, I tried using an abstract type member within the child to state the generic types of the parent type like this:

trait Parent [C <: Child] {
  def foo(c: C)
}

trait Child {
  type P <: Parent[this.type]

  def parent: P = ...
  def bar = parent.foo(this)
}

This isn't working when I try to implement it:

class ActualParent extends Parent [ActualChild] {
  def foo(c: ActualChild) = ...
}

class ActualChild extends Child {
  type P = ActualParent
}

Gives the following error:

overriding type Parent in trait Child with bounds >: Nothing <: Parent[ActualChild.this.type]
type Parent has incompatible type

What does that mean?

like image 532
Marcus Downing Avatar asked Dec 12 '22 19:12

Marcus Downing


1 Answers

You can use the approach given in http://programming-scala.labs.oreilly.com/ch13.html:

abstract class ParentChildPair {
  type C <: Child
  type P <: Parent

  trait Child {self: C =>
    def parent: P
  }

  trait Parent {self: P =>
    def child: C
  }
}

class ActualParentChildPair1 {
  type C = Child1
  type P = Parent1

  class Child1 extends Child {...}

  class Parent1 extends Parent {...}
}
like image 187
Alexey Romanov Avatar answered Jan 08 '23 14:01

Alexey Romanov