开发者

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

开发者 https://www.devze.com 2023-01-19 07:46 出处:网络
Update: Clarified and expanded, since the original question w开发者_StackOverflowas simplified too far

Update: Clarified and expanded, since the original question w开发者_StackOverflowas 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?


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


It can be done with abstract type members.

class Parent {
  type C <: Child
  def child: C = null.asInstanceOf[C]
}

class Child {
  type P <: Parent
  def parent: P = null.asInstanceOf[P]
}


Further to @Daniel's answer, I can use 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.type isn't usable directly in generics, but in a parameter it seems to be fine. This approach is a lot less long-winded than the surrounding abstract class, and allows more flexible uses such as children that are also parents.


You can write C <: Child[_].


Even though it wasn't successful, I'll record this avenue as an answer.

Using abstract type members, state bounds for the types referring back to this.type:

trait Parent {
  type C <: Child { type P <: this.type }
  def foo(c: C)
}

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

class ActualParent extends Parent {
  type C = ActualChild
  def foo(c: ActualChild) = println("Hello")
}

class ActualChild extends Child {
  type P = ActualParent
  def parent = new ActualParent
}

The problem here is that the compiler doesn't connect this with the child type of the parent, so calling parent.foo(this) results in:

type mismatch
found : Child.this.type (with underlying type Child)
required: _3.C where val _3: Child.this.P
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号