Returning the same type from abstract traits method

Let's say that we have a trait that has some meanings and some operations on them.

trait Foo {
  type Self <: Foo
  val x: Int
  def withX(x: Int): Self


This is implemented using abstract types. We have a type binding to Self and can implement it like this:

case class Foo1(x: Int) extends Foo {
  type Self = Foo1
  def withX(x: Int) = copy(x = x)


It's fine. We can use the method and we can see that the type is statically persisted.

scala> Foo1(10).withX(5)
res0: Foo1 = Foo1(5)


Problems start when we want to have an operation on a type rather than a specific type:

object Foo {
//Error:(13, 43) type mismatch;
//found   : f.Self
//required: A
//  def setFive[A <: Foo](f: A): A = f.withX(5)


Well, we can't do that because the compiler doesn't know what type of Foo # Self will be assigned. But we know that this is the same type.

Of course, using the ugly approach works great:

object Foo {
  // Ugly type signature
  def setFiveValid[A <: Foo](f: A): A#Self = f.withX(5)

  // Another ugly type signature
  def setFiveValid2[A <: Foo](f: A): f.Self = f.withX(5)


None of them express intent very clearly.

We can work with it using type classes.

case class Foo2(x: Int)

trait FooOps[A] extends Any {
  def a: A
  def withX(x: Int): A

object Foo2 {
  implicit class Foo2Ops(val a: Foo2) extends AnyVal with FooOps[Foo2] {
    def withX(x: Int) = a.copy(x = x)

object Foo {
  // View bounds approach.
  def setFiveValid3[A <% FooOps[A]](f: A): A = f.withX(5)


However, it is still very noisy.

Is there a better way to implement this setFive


Edit 1

The main problem with native types is this:

Error:(24, 11) type mismatch;
 found   :[self.Self] =>[self.Self]
    (which expands to)[(, self.Self)] =>[(, self.Self)]
 required:[self.Self] =>[(, Self)]
    (which expands to)[(, self.Self)] =>[(, Self)]


Which then resort to weird looking signatures and patterns:

  def attackReachable(
    data: WObject.WorldObjUpdate[Self]
  ): WObject.WorldObjUpdate[data.value._2.Self]



source to share

2 answers

You can go down the road with F-boundaries:

trait Foo[F <: Foo[F]] {
  def withX(x: Int): F

object Foo {
  def setFive[F <: Foo[F]](f: F): F = f.withX(5)


I've used this with success, but it's worth writing F <: Foo[F]]




The best signature is the path-dependent type you suggested:

// Another ugly type signature
def setFiveValid2[A <: Foo](f: A): f.Self = f.withX(5)


You don't even need a type parameter. Enter f

as Foo

(unless you need it A

for something else in your real context):

def setFiveValid3(f: Foo): f.Self = f.withX(5)


It's not ugly. This is one of the ideal uses for path dependent types, conversely. I also disagree when you say that it does not explicitly express intent: you are clearly stating that the result will be of the type of Self

argument you give.



All Articles