Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Scala have classes when it already has traits?

This may seem like a silly question, so bear with me...

Consider this REPL session:

scala> trait T
defined trait T

scala> val t = new T
<console>:8: error: trait T is abstract; cannot be instantiated
       val t = new T
               ^

scala> val t = new T {}
t: java.lang.Object with T = $anon$1@78db81f3

scala> class C
defined class C

scala> val c = new C
c: C = C@170a6001

We can use a trait just like a class, except that we have to add {} after the new T. In fact, we're essentially mixing T into java.lang.Object, which actually makes a lot of sense to me.

If we have members, again only the {} must be added:

scala> trait T2 { val s = "test" }
defined trait T2

scala> val t2 = new T2
<console>:8: error: trait T2 is abstract; cannot be instantiated
       val t2 = new T2
                ^

scala> val t2 = new T2 {}
t2: java.lang.Object with T2 = $anon$1@6a688d6f

scala> t2.s
res0: java.lang.String = test

scala> class C2 { val s = "test" }
defined class C2

scala> val c2 = new C2
c2: C2 = C2@73ea7821

scala> c2.s
res1: java.lang.String = test

If we have abstract members then the trait declaration is actually shorter by a few characters and, more importantly, more consistent in my eyes (no need to remember to put abstract in front of your declarations):

scala> trait T3 { val s: String }
defined trait T3

scala> val t3 = new T3 { val s = "test" }
t3: java.lang.Object with T3 = $anon$1@1f2f0ce9

scala> abstract class C3 { val s: String }
defined class C3

scala> val c3 = new C3 { val s = "test" }
c3: C3 = $anon$1@207a8313

If you forget that you must define some of the members, both ways give you compile errors:

scala> val badt3 = new T3 {}
<console>:7: error: object creation impossible, since value s in trait T3 of type String is not defined
       val badt3 = new T3 {}

scala> class BadC3 { val s: String }
<console>:8: error: class BadC3 needs to be abstract, since value s is not defined
       class BadC3 { val s: String }

And if we try to do more complex things then the power of traits naturally becomes further apparent:

scala> val t4 = new T with T2
t4: java.lang.Object with T with T2 = $anon$1@479e0994

scala> val c4 = new C with C2
<console>:9: error: class C2 needs to be a trait to be mixed in
       val c4 = new C with C2

So again I ask, why does Scala bother with classes at all when traits are apparently both simpler and more powerful?

I assume the reason is conceptual and actual compatibility with Java, but I wonder whether code compatability could have been maintained behind the scenes. As I understand it, Scala traits just become Java classes behind the scenes, so why couldn't the reverse happen and Scala consider Java classes to essentially be traits?

Related to all this, why not allow dropping the curly brackets when unnecessary? For example:

val t = new T

At that point, as a user, traits would be indistinguishable from current Scala classes, but of course better.

like image 417
pr1001 Avatar asked Dec 04 '11 11:12

pr1001


People also ask

What is the difference between class and trait in Scala?

Traits are similar in spirit to interfaces in Java programming language. Unlike a class, Scala traits cannot be instantiated and have no arguments or parameters. However, you can inherit (extend) them using classes and objects.

Why do we use traits in Scala?

Traits are used to define object types by specifying the signature of the supported methods. Scala also allows traits to be partially implemented but traits may not have constructor parameters. A trait definition looks just like a class definition except that it uses the keyword trait.

Can a trait extend a class Scala?

Yes they can, a trait that extends a class puts a restriction on what classes can extend that trait - namely, all classes that mix-in that trait must extend that class .

What is the difference between trait and interface in Scala?

Like a class, Traits can have methods(both abstract and non-abstract), and fields as its members. Traits are just like interfaces in Java. But they are more powerful than the interface in Java because in the traits we are allowed to implement the members.


1 Answers

There are several differences between traits and classes:

  • a trait can not take constructor parameters. This limitation might be lifted at some point, but it's a hard problem. A trait may be inherited multiple times in a hierarchy, and each instantiation may give different values for the constructor parameters

  • a trait is compiled to a Java interface and an implementation class (carrying the concrete methods). This means it's a bit slower, because all calls go through interfaces, and if they're concrete, they are forwarded to their implementation

  • a trait with concrete members can't be nicely inherited in Java (it could, but it would look like an interface, therefore concrete members would still need to be implemented in Java).

I don't think the distinction between classes and traits will go away, mostly because of the last two items. But they may become easier to use if the first point is solved. Regarding instantiation without the {}, that's a convenience that could be added, but I personally wouldn't like it: each instantiation creates a new class (an anonymous one), and there should be an indication to the programmer that that's the case.

like image 63
Iulian Dragos Avatar answered Oct 05 '22 10:10

Iulian Dragos