Scala trivia of the day: Traits can extend classes

A lot of people seem to not know this. In particular 90% of use of self types I’ve seen appear to exist solely because people do not know this.

Observe the following interpreter session:

scala> class Foo;
defined class Foo

scala> class Bar;
defined class Bar

scala> trait Stuff extends Foo;
defined trait Stuff

scala> new Foo with Stuff;
res0: Foo with Stuff = [email protected]

scala> new Bar with Stuff;
<console>:8: error: illegal inheritance; superclass Bar
is not a subclass of the superclass Foo
of the mixin trait Stuff
new Bar with Stuff;
^

You can basically view this as putting a constraint on the trait, saying that all classes that implement this trait must extend this superclass. This can be particularly useful for adding various sorts of behaviour to classes. e.g. traits which add behaviours to GUI components.

Thus ends our public service announcement.

This entry was posted in programming and tagged on by .

10 thoughts on “Scala trivia of the day: Traits can extend classes”

1. Larry Yogman

The distinction between self-types and inheritance sometimes eludes me. Perhaps I’m missing some meaningful difference?

Is there a good reason why Scala needs self-types? Wouldn’t it be possible to have a language where any type – even an abstract type or type parameter – could appear after ‘extends’?

2. david Post author

In all honesty, I don’t use self types. 90% of their uses are equivalent to inheritance and I consider the remaining 10% to be antipatterns. Their primary benefit is that inheritance is forced to form a DAG, but self types allow cycles.

3. david Post author

Actually, I guess that’s not true. There’s a 1% of use cases involving, as you say, type parameters and members. But I don’t think I’ve ever seen that done in the wild.

4. Larry Yogman

Cycles: I think I see it now. Self-types let you break a BigMess up into a lot of smaller traits, all with self-types saying “I’m part of BigMess”. Then BigMess can inherit all the parts, closing the cycles.

I think I even did that once, but I refactored to a DAG after the next time I had to read the code.

5. david Post author

Yeah. The problem is that breaking it up like that doesn’t actually reduce the complexity of the BigMess. It just lets you pretend it does until you’re actually forced to deal with it.

6. Dave

In practice, I only use self-types for cake. In some ways it’s just a matter of emphasis. I use inheritance for “is-a” relationships, and self-types for “depends-on” relationships. Using mix-in inheritance for component composition obscures this distinction, but separating them this way allows a way to document the difference.

7. Alain O'Dea

Self-types have an important benefit that is missed in an analysis of whether or not Scala needs them to express a particular intent. Dave’s point is one part in that he clearly explains the value of self-types for distinctly identifying is-a vs depends-on.

Using inheritance in place of self-types for depends-on relationships has an important drawback: it exposes the dependencies to client code of the component with dependencies. If Stuff is intended to provide purely encapsulated behaviour, then exposing it to client code is a design flaw and self-types should be preferred. If Stuff intentionally provides an extended public API to client code then inheritance has to be used.

Clearly inheritance can be used for both cases, but it strikes me as bad design to use it for dependency management.

8. Magnus

I agree with Dave and Alain O’Dea that self types are very useful. Alain O’Dea’s explanation is very good! Of course one should only use self types when one understands that they’re appropriate to use, not just because they happen to be there…