My point is that if you use abstract vals
or defs
then you do not need constructors.
As such everything can be a trait or a final class / object; which can help you with your linearization issues.
Something like:
// instead of
abstract class FooImpl(bar: Int) {
final def baz(x: Int): Int = bar * x
}
final case class Foo(bar: Int) extends FooImpl(bar)
// you can
trait FooImpl {
def bar: Int
final def baz(x: Int): Int = bar * x
}
final case class Foo(override final val bar: Int) extends FooImpl
Also, as @sangamon said, whereas your use case is legitimate. Things like ADTs with a lot of methods and depending on linearization order seems too OOP to me and very complex.
As I have mentioned before, for me everything is either abstract or final, I rarely find myself overriding something concrete and I have never needed to call my parent implementation, I personally would find that a nightmare to understand and change.
But, whatever, do not let my mental limitations to guide you
About the private a self types, I guess you can see it here the reason for both is not exposing those to external users and avoiding mistakes like extending FooImpl
from Bar
.