Posted on by & filed under Content - Highlights and Reviews, Programming & Development.

Many programmers exhibit a slight, involuntary shudder whenever multiple inheritance is mentioned. Ask them to identify the source of their unease and they will almost certainly finger the so-called Deadly Diamond of Death inheritance pattern. In this pattern, classes B and C both inherit from class A, while class D inherits from both B and C. If B and C both override some method from A, there is uncertainty as to which implementation D should inherit.

Different languages solve this uncertainty in different ways. C++ implements a system in which developers must manually resolve conflicts by overriding each conflicted method (A’s entire interface) at the “bottom point” of the diamond (D in this case). The implementation in D has the ability to delegate to the implementation in B and to the implementation in C. Strangely, D will also have two complete copies of A’s fields: one for B’s methods to operate on and one for C’s methods. In practice, diamond inheritance in C++ usually ends up resulting in exceedingly verbose and (at least) slightly hard-to-understand code.

The implementors of Java and the .Net languages found C++’s system so distasteful that they decided to forgo implementing multiple inheritance entirely. But this post is about Scala, and how it manages to support multiple inheritance in an easier-to-understand way, without the headaches incurred by C++’s technique, adding complexity only when you need to express complex delegation sequences. In order to understand multiple inheritance in Scala, though, we first have to talk about a feature called traits, since Scala does not permit a class to inherit from more than one other class (we will see that only traits are multiple-inheritable).

Traits

Traits in Scala are best described as “interfaces that can provide concrete members.” Read more about traits in scala in Scala in Action. First, let’s look at the “interfaces” part. Traits can have abstract members (both fields and methods), so you can use them like you use interfaces in Java. In fact, Scala offers traits instead of interfaces (with all-method pure-abstract traits and Java interfaces being bytecode-compatible for interoperability). However, traits, unlike interfaces in Java, can provide concrete members. This is a pretty major difference, so let’s take a minute to think about the implications of allowing interfaces to provide concrete members.

In addition to serving as pure interfaces, traits can also provide bundles of functionality like Java’s AbstractFoo (e.g. AbstractList, etc.) classes. That is, a trait can have a few abstract methods and a number of concrete members implemented in terms of those abstract members. By mixing in a trait and implementing the abstract members, a class gets its concrete members for free. Let’s look at a simple example:

This example is a little bit contrived. This is not a particularly good way to do logging (not to mention the fact that we are throwing away the stack traces from our exceptions), but the point should be clear: by mixing in the Logger trait and implementing the abstract print method, ConsoleLogger gains the concrete info, warning, and error methods.

A less-obvious issue with AbstractList and friends is that, since inheriting from one of them means that you cannot inherit from any other class class, they tend to have huge footprints. This footprint forces you to implement a large number of not-necessarily-related abstract methods and receive a large bundle of concrete methods, some of which may be unwanted (the sort of situation that creates composition-vs-inheritance zealots). It makes sense that they would be defined that way: if you are going to be the one-and-only helper class some child class inherits from, you had better help that child class a lot. Traits, however, do not suffer from this issue. They are interface-y, and multiple traits can be mixed into a single class, allowing a much more fine-grained, pseudo-compositional approach. Each trait then adds a small piece of functionality (to see this taken to its logical conclusion, check out the Cake Pattern). This brings us back to multiple inheritance. And I haven’t yet explained that easier solution to the Diamond Problem that I promised.

Solving the Diamond Problem

Scala’s solution to the Diamond Problem is actually fairly simple: it considers the order in which traits are inherited. If there are multiple implementors of a given member, the implementation in the supertype that is furthest to the right (in the list of supertypes) “wins.” Of course, the body of the class or trait doing the inheriting is further to the right than the entire list of supertypes, so it “wins” all conflicts, should it provide an overriding implementation for a member.

Let’s see Scala’s multiple inheritance in action with a classic example:

When executed, this code will print “A pretty painting!,” since a CowboyArtist mixes in Artist after it mixes in Cowboy.

Traits as Types

One extremely convenient thing about the “with” syntax is that it can also be used in type annotations:

This provides a lightweight syntax for requiring an argument to mix in a specific set of traits (implement a specific set of interfaces). If you’ve ever tried to write a method in Java that takes a parameter that both extends a class and implements an interface, this syntax should be extremely appealing. Like with Java, you would have to use a generic to achieve a similar effect. Note that the order that the traits are specified in doesn’t matter here; Argument objects must mix in all of the specified traits, but they may be mixed in any order.

Delegating to Super

In classes that inherit, it is common to delegate to members of super. In Scala, traits can also delegate to super. This leads one to wonder what type of object super actually refers to within a trait. At compile-time, this is easy to determine: it is whatever type the trait extends (or AnyRef if it does not specify a type to extend). As is often the case, though, super’s runtime type is often a subtype of its compile-time type.

As a rule of thumb, the runtime type for super can be found by looking “left” in the supertype list of the instantiated (leaf) class. To be sure of super’s exact runtime type, though, we have to apply a process called “type linearization.” In performing a linearization, you start with the type of the instantiated (leaf) class and recursively expand each of its supertypes into a list of their supertypes (the resulting list should be flat, not nested). You then scan that left-to-right and, whenever you come across a type that you’ve already seen, you delete it from the list. Consider the following type hierarchy:

We start with the leaf type, C. It expands to:

This expands to (grouped for readability: it’s still all one list):

After scanning and removing duplicates, we get:

From within a given trait (or class), the runtime type of super is given by the longest prefix of the final linearized type that ends does not include that trait (or class). For example, within A, super has the exact type:

From this, we can see that running the application above would produce:

You might wonder why “Base2” is never printed. However, by inspecting the type definitions and the linearization above, you will see that Base2 is left of Base1 in the linearization, meaning that it can only be called if Base1 delegates to super, but this does not happen.

If you are still having trouble with type linearization, don’t be too worried: you don’t need to have a perfect understanding of it unless you are concerned with the exact order in which delegations to super are executed.

Wrapping Up

I hope that this post has given you some clarity as to how Scala implements multiple inheritance. Its system starts simple and adds complexity only when you actually need to do complex things, unlike C++’s system, which is complex and verbose from the start.

Safari Books Online has the content you need

Check out these Scala books available from Safari Books Online:

Scala in Action is a comprehensive tutorial that introduces Scala through clear explanations and numerous hands-on examples. Because Scala is a rich and deep language, it can be daunting to absorb all the new concepts at once. This book takes a “how-to” approach, explaining language concepts as you explore familiar programming challenges that you face in your day-to-day work.
This book takes a step-by-step tutorial approach to teaching you Scala. Starting with the fundamental elements of the language, Programming in Scala introduces functional programming from the practitioner’s perspective, and describes advanced language features that can make you a better, more productive developer.
Scala in Depth is a unique new book designed to help you integrate Scala effectively into your development process. By presenting the emerging best practices and designs from the Scala community, it guides you though dozens of powerful techniques example by example.

About the author

Chris Hodapp is nearing the completion of a Master’s degree in Computer
Science at the University of Alabama, the same school from which he holds an
undergraduate CS degree. Since completing a Google Summer of Code with the
Scala team, he has spent most of his free time looking for ways to do interesting
things both with and to Scala. Chris can be reached on Twitter, where he is
@clhodapp.

Tags: C#, Deadly Diamond of Death, Multiple Inheritance, patterns, Scala, Super, Traits, types,

Comments are closed.