0

I frequently come across a problem where I need a member in an implementation of an abstract class to indicate it will return the concrete type. I'm sure I'm missing something obvious here.

This is a trivial example that indicates the problem:

object Poop {
  def getValue[T <: Poop](x: T): T = x.aValue
}

abstract class Poop {
  val aValue: Poop
}

This fails to compile the x.aValue expression with "Expression of type Poop doesn't conform to expected type T", obviously because the compiler doesn't know what the real type of aValue will be.

How do define my abstract class so the aValue will be the concrete type? Or should I be modelling problems like this differently?

1 Answer 1

4

This can be achieved with F-bounded polymorphism:

object Poop {
  def getValue[T <: Poop[T]](x: T): T = x.aValue
}

abstract class Poop[Self <: Poop[Self]] {
  val aValue: Self

  // No need for an external method. This will return the concrete type.
  def getValue: Self = aValue
}

class Foo extends Poop[Foo] {
  val aValue: Foo = this
}

It's not always convenient to have the abstract class with a type parameter. In that case it's possible to define the concrete type as a type member:

object Poop {
  // Helper type that provides access to the concrete type with a type parameter.
  type Aux[C <: Poop] = Poop { type Self = C }

  def getValue[T <: Poop.Aux[T]](x: T): T = x.aValue
}

abstract class Poop {
  type Self <: Poop.Aux[Self]

  val aValue: Self

  def getValue: Self = aValue
}

class Foo extends Poop {
  type Self = Foo

  val aValue: Foo = this
}
1
  • You're right in that the object getValue isn't needed, that was just to demonstrate the problem. I tried your first solution on a real case and it works (of course). Commented Oct 29, 2018 at 23:12

Not the answer you're looking for? Browse other questions tagged or ask your own question.