Scala: Trait Mixin with Abstract Base Class
My understanding is that while the error message may be confusing, the behaviour is correct.
foo
is declared as abstract override
in StackingTrait
, and thus in any concrete class that mixes StackingTrait
there must be a concrete (not marked as abstract
) implementation of foo
before StackingTrait
(relative to the linearization order). This is because super
refers to the trait just before in the linearization order, so there definitely needs to be a concrete implementation of foo
before StackingTrait
is mixed in, or super.foo
would be nonsensical.
When you do this:
class Impl extends Base with StackingTrait {
def foo {}
}
the linearization order is Base
<- StackingTrait
<- Impl
. The only trait before StackingTrait
is Base
and Base
does not define a concrete implementation of foo
.
But when you do this:
traitImplHelper extends Base {
def foo {}
}
class Impl extends ImplHelper with StackingTrait
The linearization order becomes: Base
<- ImplHelper
<- StackingTrait
<- Impl
Here ImplHelper
contains a concrete definition of foo
, and is definitly before StackingTrait
.
For what is worth, if you had mixed ImplHelper
after StackingTrait
(as in class Impl extends StackingTrait with ImplHelper
) you would again have the same problem and it would fail to compile.
So, this look fairly consistent to me.
I am not aware of a way to make it compile as you intended to. However if you are more concerned about making it easier to write Impl
(and being able to define foo
right there without a need for a separate class/trait) than making it easy to write Base
or StackingTrait
, you can still do this:
trait Base {
protected def fooImpl
def foo { fooImpl }
}
trait StackingTrait extends Base {
abstract override def foo { super.foo }
}
class Impl extends Base with StackingTrait {
protected def fooImpl {}
}
Just like in the original version you force each concrete class to implement foo
(in the form of fooImpl
) and this time it does compile.
The downside here is that while fooImpl
must not call super.foo
(it makes no sense and will go into an infinite loop), the compiler won't warn you about it.