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?
CodePudding user response:
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.