Scala Trait Returning a Sub-type?

162 Views Asked by At

Given a trait:

@ trait Foo {
    def withStuffAdded(str: String): Foo
  } 
defined trait Foo

I then defined a sub-class of Foo:

@ class X(val str: String) extends Foo { 
    override def withStuffAdded(s: String): Foo = new X(str + s)
  } 
defined class X

Finally, I tried to define this function:

@ def something(x: X): X = x.withStuffAdded("something") 
cmd3.sc:1: type mismatch;
 found   : ammonite.$sess.cmd1.Foo
 required: ammonite.$sess.cmd2.X
def something(x: X): X = x.withStuffAdded("something")
                                         ^
Compilation Failed

The compiler fails as it expected a Foo to be returned, not an X.

How can I change the definition of Foo#withStuffAdded to compile, i.e. return a sub-type of Foo in the type signature?

1

There are 1 best solutions below

7
On BEST ANSWER

Luis' comment is perfectly valid. I'll try explaining how to arrive to the solution mentioned in the comment.

If I got this right, you are actually interested in having the subclass as a result type in the subclass' method, but since your trait definition does not allow it, you are forced to return a Foo in method withStuffAdded of class X.

Although not obvious, this sounds like a code smell. Perhaps an abstract type is what are you looking for to begin with, to loosen up the design and allow your requirement to comply:

trait Foo {
  type T <: Foo

  def withStuffAdded(str: String): T
}

class X(val str: String) extends Foo {
  type T = X

  override def withStuffAdded(s: String): X = new X(str + s)
}

Now this will work:

def something(x: X): X = x.withStuffAdded("something")

Although a flexible design, from here on, it becomes clearer that in order to further improve on it, possible solutions include either using f-bounded polymorphism or a typeclass.

I would probably be inclined to use a typeclass as this is the functional way. What you'll need depends on your requirements, but either will work.