Why do we need traits in scala?

Viewed 2000

So, I was trying to make a finagle server, talk to sentry (not important), and stumbled upon a case, where I needed to inherit from two classes (not traits) at the same time, let's call them class SentryHandler extends Handler and class TwitterHandler extends Handler, and assume, that I need to create MyHandler, that inherits from both of them.

After a moment of stupidity, when I thought it was impossible without using a dreaded "delegation pattern", I found a solution:

trait SentryTrait extends SentryHandler
class MyHandler extends TwitterHandler with SentryTrait

Now, this got me thinking: what is the purpose of having the notion of "trait" to being with? If the idea was to enforce that you can inherit from multiple traits but only a single class, it seems awfully easy to get around. It kinda sounds like class is supposed to be the "main" line of inheritance (that you "extend a class with traits", but that isn't true either: you can extend a trait with (or without) a bunch of other traits, and no class at all.

You cannot instantiate a trait, but the same holds for an abstract class ...

The only real difference I can think of is that a trait cannot have constructor parameters. But what is the significance of that? I mean, why not? What would the problem with something like this?

class Foo(bar: String, baz: String) extends Bar(bar) with Baz(baz) 
4 Answers

The problem with having constructors and state in a trait (which then makes it a class) is with multiple inheritance. While this is technically possible in a hypothetical language, it is terrible for language definition and for understanding the program code. The diamond problem, mentioned in other responses to this question), causes the highest level base class constructor to be called twice (the constructor of A in the example below).

Consider this code in a Scala-like language that allows multiple inheritance:

Class A(val x: Int)
class B extends A(1)
class C extends A(2)
class D extends B, C

If state is included, then you have to have two copies of the value x in class A. So you have two copies of class A (or one copy and the diamond problem - so called due to the diamond shape of the UML inheritance diagram).

Diamond Multiple Inheritance

The early versions of the C++ compiler (called C-Front) had lots of bugs with this and the compiler or the compiled code often crashed handling them. Issues include if you have a reference to B or C, how do you (the compiler, actually) determine the start of the object? The compiler needs to know that in order to cast the object from the Base type (in the image below, or A in the image above) to the Descendant type (D in the image above).

Multiple Inheritance Memory Layout

But, does this apply to traits? The way I understand it, Traits are an easy way to implement composition using the Delegation Pattern (I assume you all know the GoF patterns). When we implement Delegation in any other language (Java, C++, C#), we keep a reference to the other object and delegate a message to it by calling the method in its class. If traits are implemented in Scala internally by simply keeping a reference and calling its method, then traits do exactly the same thing as Delegation. So, why can't it have a constructor? I think it should be able to have one without violating its intent.

Related