I started using Scala few weeks ago. Overall I really like all the features that this language gives to the developer, but it is hard to switch from Java habits sometimes.
This question is concerning traits. Currently almost everytime I see some part of logic that I would extract to different class in Java and add constructor parameter to dependent class I am switching to creating trait and mixing it in my class. (if all you have is a hammer, everything looks like a nail)
But I see two problems with my approach.
Testing classes with mixed in traits:
In Java if I would have class Foo that would use Bar and Baz classes I would probably inject them to my Foo class and use them. If I do this using traits I will have class Foo extends Bar with Baz.
Now in my tests I would have to write something like
trait BarMock extends Bar{
override def bar = "barMock"
}
trait BazMock extends Baz{
override def baz = "bazMock"
}
val foo = new Foo with BarMock with BazMock
if I want to swap the implementation of some trait behaviour. Maybe it is just me that is used to writing
Bar bar = mock(Bar.class);
when(bar.bar()).thenReturn("barMock");
Baz baz = mock(Baz.class);
when(baz.baz()).thenReturn("bazMock");
Foo foo = new Foo(bar, baz);
Is it normal to mock traits like I showed in Scala projects? Or I should restrain myself from mixing in mocked traits in tests.
Trait method visibility
This is also connected with my current obsession with traits. In Java if I inject Bar, Baz instances to my Foo instance, I am not automatically adding all public methods from Bar, Baz to my Foo interface. If I want to do it, I have to add each delegating method hand by hand. If I am using traits, when I mix in some trait, I am automatically "polluting" Foo interface with methods from my traits. Is there any way I could achieve something like private inheritance from C++?
The only solution that comes to my mind is declaring trait methods as protected and marking the trait as package private. This way methods will be visible in Foo, but outside of the package someone can't write val bar:Bar = new Foo. Also if I understand correctly, package private constraint will be validated only when package private trait source code will be in my project, because JVM has no way of representing the concept of package private and compiler just makes it public in bytecode.
In a word: Cake. The Cake Pattern. Much has been written about it, most marginal, but once the concept clicks into your head it's pretty easy to realize. And it's a great tool for achieving SOLID as well as testability and statically typed dependency injection and mix-and-match componentized code.
There is a price to it, but where I used to feel that price demanded a specific justification to use Cake for a given bit of your code, I now see it the other way around. You should justify not using it on a case-by-case basis.
The seminal paper on Cake is Scalable Component Abstractions (that's a paywall, but it's available free elsewhere on the 'Net). Nonetheless, as with most such things, that's not the best place to start. Simply searching the Web for "Scala Cake Pattern" produces many hits. Read a few until you find one that englightens you…
Related
What is the advantage of using an abstract class instead of a trait (apart from performance)? It seems like abstract classes can be replaced by traits in most cases.
I can think of two differences
Abstract classes can have constructor parameters as well as type parameters. Traits can have only type parameters. There was some discussion that in future even traits can have constructor parameters
Abstract classes are fully interoperable with Java. You can call them from Java code without any wrappers. Traits are fully interoperable only if they do not contain any implementation code
There's a section in Programming in Scala called "To trait, or not to trait?" which addresses this question. Since the 1st ed is available online, I'm hoping it's OK to quote the whole thing here. (Any serious Scala programmer should buy the book):
Whenever you implement a reusable collection of behavior, you will
have to decide whether you want to use a trait or an abstract class.
There is no firm rule, but this section contains a few guidelines to
consider.
If the behavior will not be reused, then make it a concrete class. It
is not reusable behavior after all.
If it might be reused in multiple, unrelated classes, make it a trait.
Only traits can be mixed into different parts of the class hierarchy.
If you want to inherit from it in Java code, use an abstract class.
Since traits with code do not have a close Java analog, it tends to be
awkward to inherit from a trait in a Java class. Inheriting from a
Scala class, meanwhile, is exactly like inheriting from a Java class.
As one exception, a Scala trait with only abstract members translates
directly to a Java interface, so you should feel free to define such
traits even if you expect Java code to inherit from it. See Chapter 29
for more information on working with Java and Scala together.
If you plan to distribute it in compiled form, and you expect outside
groups to write classes inheriting from it, you might lean towards
using an abstract class. The issue is that when a trait gains or loses
a member, any classes that inherit from it must be recompiled, even if
they have not changed. If outside clients will only call into the
behavior, instead of inheriting from it, then using a trait is fine.
If efficiency is very important, lean towards using a class. Most Java
runtimes make a virtual method invocation of a class member a faster
operation than an interface method invocation. Traits get compiled to
interfaces and therefore may pay a slight performance overhead.
However, you should make this choice only if you know that the trait
in question constitutes a performance bottleneck and have evidence
that using a class instead actually solves the problem.
If you still do not know, after considering the above, then start by
making it as a trait. You can always change it later, and in general
using a trait keeps more options open.
As #Mushtaq Ahmed mentioned, a trait cannot have any parameters passed to the primary constructor of a class.
Another difference is the treatment of super.
The other difference between classes and traits is that whereas in classes, super calls are statically bound, in traits, they are dynamically bound. If you write super.toString in a class, you know exactly which method implementation will be invoked. When you write the same thing in a trait, however, the method implementation to invoke for the super call is undefined when you define the trait.
See the rest of Chapter 12 for more details.
Edit 1 (2013):
There is a subtle difference in the way abstract classes behaves compared to traits. One of the linearization rules is that it preserves the inheritance hierarchy of the classes, which tends to push abstract classes later in the chain while traits can happily be mixed in. In certain circumstances, it's actually preferable to be in latter position of the class linearization, so abstract classes could be used for that. See constraining class linearization (mixin order) in Scala.
Edit 2 (2018):
As of Scala 2.12, trait's binary compatibility behavior has changed. Prior to 2.12, adding or removing a member to the trait required recompilation of all classes that inherit the trait, even if the classes have not changed. This is due to the way traits were encoded in JVM.
As of Scala 2.12, traits compile to Java interfaces, so the requirement has relaxed a bit. If the trait does any of the following, its subclasses still require recompilation:
defining fields (val or var, but a constant is ok – final val without result type)
calling super
initializer statements in the body
extending a class
relying on linearization to find implementations in the right supertrait
But if the trait does not, you can now update it without breaking binary compatibility.
For whatever it is worth, Odersky et al's Programming in Scala recommends that, when you doubt, you use traits. You can always change them into abstract classes later on if needed.
Other than the fact that you cannot directly extend multiple abstract classes, but you can mixin multiple traits into a class, it's worth mentioning that traits are stackable, since super calls in a trait are dynamically bound (it is referring a class or trait mixed before current one).
From Thomas's answer in Difference between Abstract Class and Trait:
trait A{
def a = 1
}
trait X extends A{
override def a = {
println("X")
super.a
}
}
trait Y extends A{
override def a = {
println("Y")
super.a
}
}
scala> val xy = new AnyRef with X with Y
xy: java.lang.Object with X with Y = $anon$1#6e9b6a
scala> xy.a
Y
X
res0: Int = 1
scala> val yx = new AnyRef with Y with X
yx: java.lang.Object with Y with X = $anon$1#188c838
scala> yx.a
X
Y
res1: Int = 1
When extending an abstract class, this shows that the subclass is of a similar kind. This is not neccessarily the case when using traits, I think.
In Programming Scala the authors say that abstract classes make a classical object oriented "is-a" relationship while traits are a scala-way of composition.
Abstract classes can contain behaviour - They can parameterized with constructor args (which traits can't) and represent a working entity. Traits instead just represent a single feature, an interface of one functionality.
A class can inherit from multiple traits but only one abstract class.
Abstract classes can have constructor parameters as well as type parameters. Traits can have only type parameters. For example, you can’t say trait t(i: Int) { }; the i parameter is illegal.
Abstract classes are fully interoperable with Java. You can call them from Java code without any wrappers. Traits are fully interoperable only if they do not contain any implementation code.
In the Scala spec, it's said that in a class template sc extends mt1, mt2, ..., mtn
Each trait reference mti must denote a trait. By contrast, the
superclass constructor sc normally refers to a class which is not a
trait. It is possible to write a list of parents that starts with a
trait reference, e.g. mt1 with …… with mtn. In that case the
list of parents is implicitly extended to include the supertype of
mt1 as first parent type. The new supertype must have at least one
constructor that does not take parameters. In the following, we will
always assume that this implicit extension has been performed, so that
the first parent class of a template is a regular superclass
constructor, not a trait reference.
If I understand it correctly, I think it means:
trait Base1 {}
trait Base2 {}
class Sub extends Base1 with Base2 {}
Will be implicitly extended to:
trait Base1 {}
trait Base2 {}
class Sub extends Object with Base1 with Base2 {}
My questions are:
Is my understanding correct?
Does this requirement (the first subclass in the parent list must be non-trait class) and the implicit extension only applies to class template (e.g. class Sub extends Mt1, Mt2) or also trait template (e.g. trait Sub extends Mt1, Mt2)?
Why this requirement and the implicit extension is necessary?
Disclaimer: I'm not and never was a member of the "Scala design committee" or anything like that, so the answer on the "why?" question is mostly speculation but I think a useful one.
Disclaimer #2: I've written this post over several hours and in several takes so it is probably not very consistent
Disclaimer #3 (a shameful self-promotion for the future readers): If you find this quite long answer useful, you might also take a look at my another long answer to another question by Lifu Huang on a similar topic.
Short answers
This is one of those complicated things for which I don't think there is a good short answer unless you already know what the answer is. Although my real answer will be long, here are my best short answers:
Why the first base class in parent list must be non-trait class?
Because there has to be only one non-trait base class and it makes thing easier if it is always the first
Is my understanding correct?
Yes, your implicit example is what will happen. However I'm not sure that it shows full understanding of the topic.
Does this requirement (the first subclass in the parent list must be non-trait class) and the implicit extension only applies to class template (e.g. class Sub extends Mt1, Mt2) or also trait template (e.g. trait Sub extends Mt1, Mt2)?
No, implicit extensions happens for traits as well. Actually how else you could expect Mt1 to have its own "supertype" to be promoted down to the class that extends it?
Actually here are two IMHO non-obvious examples proving this is true:
Example #1
trait TAny extends Any
trait TNo
// works
class CGood(val value: Int) extends AnyVal with TAny
// fails
// illegal inheritance; superclass AnyVal is not a subclass of the superclass Object
class CBad(val value: Int) extends AnyVal with TNo
This example fails because the spec says
The extends clause extends scsc with mt1mt1 with …… with mtnmtn can be omitted, in which case extends scala.AnyRef is assumed.
so TNo actually extends AnyRef which is incompatible with AnyVal.
Example #2
class CFirst
class CSecond extends CFirst
// did you know that traits can extend classes as well?
trait TFirst extends CFirst
trait TSecond extends CSecond
// works
class ChildGood extends TSecond with TFirst
// fails
// illegal inheritance; superclass CFirst is not a subclass of the superclass CSecond of the mixin trait TSecond
class ChildBad extends TFirst with TSecond
Again ChildBad fails because TSecond requires CSecond but TFirst only provides CFirst as the base class.
Why this requirement and the implicit extension is necessary?
There are three major reasons:
Compatibility with the main target platform (JVM)
Traits have "mixin" semantics: you have a class and you mix additional behavior in
Completeness, consistency and simplicity of the rest of the spec (e.g. of linearization rules). This might be restated as following: each class must declare 0 or 1 base non-trait classes and after compilation the target platform enforces that there will be exactly 1 non-trait base class. So it makes the rest of the spec easier if you just assume there is always exactly one base class. In such way you have to write this implicit extension rules only once rather than each time when the behavior depends on the base class.
Scala spec goals/intentions
I believe that when one reads a spec there are two different sets of questions:
What exactly is written? What is the meaning of the spec?
Why it is written so? What was the intention?
Actually I think in many cases #2 is more important than #1 but unfortunately specs rarely explicitly contain insights into that area. Anyway I will start with my speculations over #2: what were the intentions/goals/limitations of the classes system in Scala? The main high-level goal was to create a type system richer than the one in Java or .Net (which are quite similar) but that can be:
compiled back to an efficient code in those target platforms
allow reasonable two-way interaction between the Scala code and the "native" code in the target platforms
Side note: Support of the .Net was dropped years ago but it was one of the target platforms for years and this affected the design.
Single base class
Short summary: this section describes some reasons why Scala designers had a strong motivation to have the "exactly one base class" rule in the language.
A major problem with OO design and particularly inheritance is that AFAIK the question: "where exactly is the border between the "good and useful" practices and the "bad" ones?" is open. It means that each language must find out its own trade off between making impossible what is wrong and making possible (and easy) what is useful. Many believe that in C++, which obviously was a major inspiration for Java and .Net, that trade off is shifted too much into "allow everything even if it is potentially harmful" zone. It made many designers of newer languages to seek for more restricting trade off. Particularly both JVM and .Net platform enforce the rule that all types are split into "value types" (aka primitive types), "classes" and "interfaces" and each class, except the root class (java.lang.Object/System.Object), has exactly one "base class" and zero or more "base interfaces". This decision was a reaction to many issues of multiple inheritance including infamous "diamond problem" but actually many others as well.
Sidenote (about memory layout): Another major problem with multiple inheritance is objects layout in memory. Consider following ridiculous (and impossible in current Scala) example inspired by Achilles and the tortoise:
trait Achilles {
def getAchillesPos: Int
def stepAchilles(): Unit
}
class AchillesImpl(var achillesPos: Int) extends Achilles {
def getAchillesPos: Int = achillesPos
def stepAchilles(): Unit = {
achillesPos += 2
}
}
class TortoiseImpl(var tortoisePos: Int) {
def getTortoisePos: Int = tortoisePos
def stepTortoise(): Unit = {
tortoisePos += 1
}
}
class AchillesAndTortoise(handicap: Int) extends AchillesImpl(0) with TortoiseImpl(handicap) {
def catchTortoise(): Int = {
var time = 0
while (getAchillesPos < getTortoisePos) {
time += 1
stepAchilles()
stepTortoise()
}
time
}
}
The tricky part here is how to actually lay achillesPos and tortoisePos fields out in the memory (of the object). The issue is that you probably want to have only one compiled copy of all the methods in the memory and you want the code to be efficient. This means that getAchillesPos and stepAchilles should have know some fixed offset of the achillesPos regarding to the this pointer. Similarly getTortoisePos and stepTortoise should have know some fixed offset of the tortoisePos regarding to the this pointer. And all choices you have to achieve this goal don't look nice. For example:
You might decide that achillesPos is always first and tortoisePos is always second. But this means that in the instances of TortoiseImpl tortoisePos should also be the second field but there is nothing to fill the first field with so you waste some memory. Moreover if both AchillesImpl and TortoiseImpl come from pre-compiled libraries, you should have some way to move access to the fields in them as well.
You might try to "fix" this pointer on-the-fly when you call into TortoiseImpl (AFAIK this is the way C++ really works). This becomes especially funny when TortoiseImpl is an abstract class that is aware of the trait Achilles (but not the specific class AchillesImpl) via extends and tries to call back some methods from there via this or pass this to some method that takes Achilles as an argument so this has to be "fixed back". Note that this is not the same as the "diamond problem" because there is only one copy of all fields and implementations.
You might agree to have a unique copy of the methods compiled for each specific class that are aware of the specific layout. This is bad for memory usage and performance because it blows CPU caches and forces JIT to make independent optimizations for each.
You might say that no method except for getter and setter can have direct access to the fields and should use getters and setters instead. Or store all the fields in some kind of a dictionary which is effectively the same. This might be bad for performance (but this is the closest to what Scala does with mixin-traits).
In the actual Scala this issue does not exist because trait can't really declare any fields. When you declare val or var in a trait, you actually declare a getter (and a setter) method(s) that will be implemented by particular class that extends the trait and each class has full control over layout of the fields. And actually in terms of performance this most probably would work OK because JVM (JIT) can inline such a virtual call in many real-world scenarios.
End of the Sidenote
Another major point is interoperability with the target platform. Even if Scala somehow supported true multiple-inheritance so you can have a type that inherits from String with Date and that can be passed to both methods that expect String and that expect Date, how this would look like from the Java point of view? Also if the target platform enforces the rule that every class has to be an (indirect) sub-type of the same root class (Object), you can't work this around in your higher level language.
Traits and Mix-ins
Many think that "one class and many interfaces" trade-off that was made in Java and .Net is too restrictive. For example it makes it hard to share common default implementation of some of the interface methods between different classes. Actually over the time Java and .Net designers seem to come to the same conclusion and rolled out they own fixes for this kind of issues: Extension methods in .Net and then Default methods in Java. Scala designers added a feature called Mixins that was known to fare well in many practical cases. However unlike many other dynamic languages that has similar feature, Scala still had to meet the "exactly one base class" rule and other limitations of the target platform.
It is important to note that there are important scenarios when mixins are used in practice is to implement a variation of the Decorator or Adapter patterns both of which relies on the fact that you can restrict your base type to something more specific than Any or AnyRef. Prime example of such usage is the scala.collection package.
Scala syntax
So now you have following goals/restrictions:
Exactly one base class for each class
Ability to add logic to classes from mixins
Support of mixins with restricted base type
Classes from the target platform (Java) when seen from Scala are mapped to the Scala classes (because what else they can be mapped to?) and they come pre-compiled and we don't want to mess with their implementation
Other good qualities such as simplicity, type safety, determinism, etc.
If you want some kind of multiple inheritance support in your language, you need to develop conflict resolution rules: what happens when several base types provide some logic that would fit the same "slot" in your class. After prohibition of fields in traits we are left with the following "slots":
Base class in terms of the target platform
Constructors
Methods with the same name and signature
And possible conflict resolution strategies are:
Prohibit (fail compilation)
Decide which one wins and wipes others
Somehow chain them
Somehow preserve all with renaming. This is not really possible in JVM. For example in .Net see Explicit Interface Implementation
In a sense Scala uses all available (i.e. first 3) strategies but the high-level goal is: let's try to preserve as many logic as we can.
The most important part for this discussion is conflicts resolution for constructors and methods.
We want the rules to be the same for different slots because otherwise it is not clear how to achieve safety (if traits A and B both override methods foo and bar but resolution rules for foo and bar are different, invariants for A and B might easily be broken). Scala's approach is based on the class linearization. In short these is the way to "flatten" hierarchy of the base classes into a simple linear structure in some predictive way that is based on the idea that the lefter type in the with chain - the more "base" (higher in the inheritance) it is. After you do this, conflict resolution rule for methods becomes simple: you go through the list of the base types and chain behavior via super calls; if super is not called, you stop chaining. This produce quite predictable semantics that people can reason about.
Now assume you allow non-trait class to be not first. Consider following example:
class CBase {
def getValue = 2
}
trait TFirst extends CBase {
override def getValue = super.getValue + 1
}
trait TSecond extends CFirst {
override def getValue = super.getValue * 2
}
class CThird extends CBase with TSecond {
override def getValue = 100 - super.getValue
}
class Child extends TFirst with TSecond with CThird
In which order TFirst.getValue and TSecond.getValue should be called? Obviously CThird is already compiled and you can't change what the super for it is, so it has to be moved to the first position and there is already TSecond.getValue call inside it. But on the other hand this breaks the rule that everything on the left is base and everything on the right is child. The simplest way to not introduce such confusion is to enforce the rule that non-trait classes must go first.
The same logic applies if you just extend the previous example by substituting class CThird with a trait that extends it:
trait TFourth extends CThird
class AnotherChild extends TFirst with TSecond with TFourth
Again, the only non-trait class AnotherChild can extend is CThird and this again makes conflict resolution rules quite hard to reason about.
That's why Scala makes a rule much simpler: whatever provides the base class must come from the first position. And then it makes sense to extend the same rule upon the traits as well so if the first position is occupied by some trait - it also defines the base class.
1) Basically yes, your understanding is correct. Like in Java, every class inherits from java.lang.Object (AnyRef in Scala). So, since you are defining a concrete class, you will implicitly inherits from Object. If you check with the REPL, you got:
scala> trait Base1 {}
defined trait Base1
scala> trait Base2 {}
defined trait Base2
scala> class Sub extends Base1 with Base2 {}
defined class Sub
scala> classOf[Sub].getSuperclass
res0: Class[_ >: Sub] = class java.lang.Object
2) Yes, from the "Traits" paragraph in the specs, this applies also to them. In "Templates" paragraph we have:
The new supertype must have at least one constructor that does not take parameters
And then in "Traits" paragraph:
Unlike normal classes, traits cannot have constructor parameters. Furthermore, no constructor arguments are passed to the superclass of the trait. This is not necessary as traits are initialized after the superclass is initialized.
Assume a trait D defines some aspect of an instance x of type C (i.e. D is a base class of C). Then the actual supertype of D in x is the compound type consisting of all the base classes in L(C) that succeed D.
This is needed to define the base constructor with no-parameters.
3) As per answer (2), it's needed to define the base constructor
I'm puzzled to choose a trait or class when writing scala code.
At first, I have a controller which with several traits:
class MyController extends Controller
with TransactionSupport
with JsonConverterSupport
with LoggerSupport
In these traits, I defined some methods and fields which can be used in MyController directly.
But my friend says: when you extends or with a trait, it should be a that trait.
Look at the MyController, it is a Controller, but it isn't a TransactionSupport, not a JsonConverterSupport, not a LoggerSupport, so it should not with them.
So the code becomes:
class MyController(tranSupport: TransactionSupport,
jsonConverter: JsonConverterSupport,
loggerSupport: LoggerSupport) extends Controller
But I don't feel good about this code, it just seems strange.
I see traits used heavily in scala code, when should I use it or use classes to inject?
I'll refer you to Interfaces should be Adjectives. Though some traits may play the part of a class (and, therefore, be nouns and respect the "is-a" relationship), when used as mixins they'll tend to play the part of interfaces.
As an "adjective", the trait will add a qualifying property to whatever they are extending. For example, they may be Comparable or Serializable.
It can be a bit hard to find an adjective to fit -- what adjective would you use for LoggerSupport? -- so don't feel overly constrained by that. Just be aware that it is completely wrong to thing of traits as necessarily an "is-a" relationship.
I would try to avoid using traits to replace "has-a" relationships, though.
My opinion is that it doesn't have to be it. Mixing-in is a different concept than inheritance. Even though syntactically it is the same, it doesn't mean the same. Typical use case for mixing-in is logging just like you wrote. It doesn't mean that if your service class mixes-in a Logging trait that it is a logger. It's just a yet another way how to compose functionality into working objects.
Odersky proposes that if you are not sure and you can, use traits because they are more flexible. You can change trait to class in the future if you need.
Sometime when I feel that mixing-in trait doesn't look good, I use module pattern like this:
trait JsonConverterModule {
protected def jsonConverter: JsonConverter
protected trait JsonConverter {
def convert(in: Json): Json
}
}
class MyController extends Controller with JsonConverterModule {
private doSmth = jsonConverter.convert(...)
}
MyController in this case looks more like a Controller, and all Json-related stuff is hidden from MyController 'client'
Your first example with traits is the "cake pattern" and your second example is "constructor injection". Both are perfectly valid ways to do dependency injection in Scala. The cake pattern is powerful, you can inject type members, the different traits can easily talk to each other (we don't have to create separate objects and pass them to each other object, often requiring setter injection rather than simple constructor injection), etc. However, the type has to be realized at compile-time, and a separate class must be realized for every combination of traits. Constructor injection lets you build your object at run-time and scales better for a large number of combinations.
There are some frameworks that fully embraces the typeclass pattern. scalaz and shapeless would be good examples. So there are certainly some cases where typeclasses are preferable over normal java classes and polymorphism.
I awe implicit evidence expression power and I'm curious why this method suffer a shortage of practical applications. What reasons compel scala programmers to use basic classes. The typeclasses obviously cost in verbosity and run-time, but is there any other reason?
I came to scala without prior java experience and wonder if I've missed some essential benefits that classic scala-java classes may give.
I'm searching for some spectacular use cases showing areas where typeclasses are insufficient or ineffective.
Typeclasses and inheritance enable reuse in different ways. Inheritance excels at providing correct functionality for changed internals.
class Foo { def foo: String = "foo" }
def fooUser(foo: Foo) { println(foo.foo) }
class Bar extends Foo {
private var annotation = List.empty[String]
def annotate(s: String) { annotation = s :: annotation }
override def foo = ("bar" :: annotation.map("#" + _)).mkString(" ")
}
Now, everyone who uses Foo will be able to get the correct value if you give them a Bar, even if they only know that the type is a Foo. You don't have to have anticipated that you might want pluggable functionality (except by not labeling foo final). You don't need to keep track of the type or keep passing a witness instance forwards; you just use Bar wherever you want in place of Foo and it does the right thing. This is a big deal. If you want a fixed interface with easily-modifiable functionality under the hood, inheritance is your thing.
In contrast, inheritance is not so great when you have a fixed set of data types with easily-modifiable interface. Sorting is a great example. Suppose you want to sort Foo. If you try
class Foo extends Sortable[Foo] {
def lt(you: Foo) = foo < you.foo
def foo = "foo"
}
you could pass this to anything that could sort a Sortable. But what if you want to sort by length of name not with the standard sort? Well,
class Foo extends LexicallySortable[Foo] with LengthSortable[Foo] {
def lexicalLt(you: Foo) = foo < you.foo
def lengthLt(you: Foo) = foo.length < you.foo.length
def foo = "foo"
}
This rapidly becomes hopeless, especially since you have to hunt down all subclasses of Foo and make sure they are updated properly. You are much better off deferring the less-than computation to a typeclass which you can swap out as needed. (Or to a regular class, which you must always reference explicitly.) This kind of automatically-selected functionality is also a big deal.
You can't really replace one with the other. When you need to easily incorporate new kinds of data to a fixed interface, use inheritance. When you need a few kinds of underlying data but need to easily supply new functionality, use type classes. When you need both, you will have a lot of work to do whichever way you go about it, so use to taste.
What is the advantage of using an abstract class instead of a trait (apart from performance)? It seems like abstract classes can be replaced by traits in most cases.
I can think of two differences
Abstract classes can have constructor parameters as well as type parameters. Traits can have only type parameters. There was some discussion that in future even traits can have constructor parameters
Abstract classes are fully interoperable with Java. You can call them from Java code without any wrappers. Traits are fully interoperable only if they do not contain any implementation code
There's a section in Programming in Scala called "To trait, or not to trait?" which addresses this question. Since the 1st ed is available online, I'm hoping it's OK to quote the whole thing here. (Any serious Scala programmer should buy the book):
Whenever you implement a reusable collection of behavior, you will
have to decide whether you want to use a trait or an abstract class.
There is no firm rule, but this section contains a few guidelines to
consider.
If the behavior will not be reused, then make it a concrete class. It
is not reusable behavior after all.
If it might be reused in multiple, unrelated classes, make it a trait.
Only traits can be mixed into different parts of the class hierarchy.
If you want to inherit from it in Java code, use an abstract class.
Since traits with code do not have a close Java analog, it tends to be
awkward to inherit from a trait in a Java class. Inheriting from a
Scala class, meanwhile, is exactly like inheriting from a Java class.
As one exception, a Scala trait with only abstract members translates
directly to a Java interface, so you should feel free to define such
traits even if you expect Java code to inherit from it. See Chapter 29
for more information on working with Java and Scala together.
If you plan to distribute it in compiled form, and you expect outside
groups to write classes inheriting from it, you might lean towards
using an abstract class. The issue is that when a trait gains or loses
a member, any classes that inherit from it must be recompiled, even if
they have not changed. If outside clients will only call into the
behavior, instead of inheriting from it, then using a trait is fine.
If efficiency is very important, lean towards using a class. Most Java
runtimes make a virtual method invocation of a class member a faster
operation than an interface method invocation. Traits get compiled to
interfaces and therefore may pay a slight performance overhead.
However, you should make this choice only if you know that the trait
in question constitutes a performance bottleneck and have evidence
that using a class instead actually solves the problem.
If you still do not know, after considering the above, then start by
making it as a trait. You can always change it later, and in general
using a trait keeps more options open.
As #Mushtaq Ahmed mentioned, a trait cannot have any parameters passed to the primary constructor of a class.
Another difference is the treatment of super.
The other difference between classes and traits is that whereas in classes, super calls are statically bound, in traits, they are dynamically bound. If you write super.toString in a class, you know exactly which method implementation will be invoked. When you write the same thing in a trait, however, the method implementation to invoke for the super call is undefined when you define the trait.
See the rest of Chapter 12 for more details.
Edit 1 (2013):
There is a subtle difference in the way abstract classes behaves compared to traits. One of the linearization rules is that it preserves the inheritance hierarchy of the classes, which tends to push abstract classes later in the chain while traits can happily be mixed in. In certain circumstances, it's actually preferable to be in latter position of the class linearization, so abstract classes could be used for that. See constraining class linearization (mixin order) in Scala.
Edit 2 (2018):
As of Scala 2.12, trait's binary compatibility behavior has changed. Prior to 2.12, adding or removing a member to the trait required recompilation of all classes that inherit the trait, even if the classes have not changed. This is due to the way traits were encoded in JVM.
As of Scala 2.12, traits compile to Java interfaces, so the requirement has relaxed a bit. If the trait does any of the following, its subclasses still require recompilation:
defining fields (val or var, but a constant is ok – final val without result type)
calling super
initializer statements in the body
extending a class
relying on linearization to find implementations in the right supertrait
But if the trait does not, you can now update it without breaking binary compatibility.
For whatever it is worth, Odersky et al's Programming in Scala recommends that, when you doubt, you use traits. You can always change them into abstract classes later on if needed.
Other than the fact that you cannot directly extend multiple abstract classes, but you can mixin multiple traits into a class, it's worth mentioning that traits are stackable, since super calls in a trait are dynamically bound (it is referring a class or trait mixed before current one).
From Thomas's answer in Difference between Abstract Class and Trait:
trait A{
def a = 1
}
trait X extends A{
override def a = {
println("X")
super.a
}
}
trait Y extends A{
override def a = {
println("Y")
super.a
}
}
scala> val xy = new AnyRef with X with Y
xy: java.lang.Object with X with Y = $anon$1#6e9b6a
scala> xy.a
Y
X
res0: Int = 1
scala> val yx = new AnyRef with Y with X
yx: java.lang.Object with Y with X = $anon$1#188c838
scala> yx.a
X
Y
res1: Int = 1
When extending an abstract class, this shows that the subclass is of a similar kind. This is not neccessarily the case when using traits, I think.
In Programming Scala the authors say that abstract classes make a classical object oriented "is-a" relationship while traits are a scala-way of composition.
Abstract classes can contain behaviour - They can parameterized with constructor args (which traits can't) and represent a working entity. Traits instead just represent a single feature, an interface of one functionality.
A class can inherit from multiple traits but only one abstract class.
Abstract classes can have constructor parameters as well as type parameters. Traits can have only type parameters. For example, you can’t say trait t(i: Int) { }; the i parameter is illegal.
Abstract classes are fully interoperable with Java. You can call them from Java code without any wrappers. Traits are fully interoperable only if they do not contain any implementation code.