Inheritance and traits - scala

I found this reading another question:
"[...] 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"
a little example:
class C
trait U
trait T extends C
class D extends U with T // does not work
class E extends T with U // works
Apparently when traits inherit from classes, you have to put the trait in the position that you would otherwise place a class in (i.e. directly after extends)
Now to my questions:
(extended the previous example)
class C
class Test
trait U
trait T extends C
trait Tst extends Test
class D extends U with T // does not work
class E extends T with U // works
class Test2 extends Tst with T
what do we do when we want to inherit form two different traits, where each of those two inherit from a different class? (see class Test 2) this doesn't seem to be possible
if we need to pay attention to the placement of traits that extend a class, how do traits work then? Are traits inheriting from classes not "normal" traits anymore?

This is indeed impossible. You cannot inherit from two different classes at once, whether via traits or otherwise. This is unfortunate, but true. There is no very good reason for that AFAICT, it would be somewhat harder to implement, but not impossible, at least for scala (not "native java") classes. So apparently, it was just decided against at some point, seemingly without a good reason.
Traits that extend classes aren't really "abnormal". It's more like the way scala compiler handles them is. Basically, anything, that is or extends a class has to appear in the extends clause, and not in with. Why? Well, why can you not compare Strings in java with ==? because ...

Here's my guess: class can be defined with parameters, now let's imagine this:
class A(val x: Int)
class B extends A(1)
class C extends A(2)
trait D extends B
trait E extends C
// Oops! x = ? Doesn't compile of course
class F extends D with E
UPDATE:
Disclaimer: I'm not an expert in C++
Here's how C++ is solving the diamond problem:
class A {
int x;
public:
A(int _x) { x = _x; }
int getX() { return x; };
};
class B : virtual public A {
public:
// B b; b.getX == 1
B() : A(1) {}
};
class C : virtual public A {
public:
// C c; c.getX == 2
C() : A(2) {}
};
class D : public B, public C {
public:
// I need to know that B/C inherit A
// A(...) constructors defined above don't apply
D(): B(), C(), A(3) {}
};
I guess that's exactly the problem Scala is trying to avoid.

Related

Understand the logic when overriding concrete method by abstract method

First of all, I realize that it doesn't make much sense to override a concrete method in superclass by an abstract method in subclass. But... since in Scala it is actually possible to do this, I tried the following snippet, and the result's getting me confused.
First scenario
The concrete method to be overridden in super-super-class
The abstract method is in super-class
class A {
def x: String = "A"
}
abstract class B extends A {
def x: String
}
class C extends B {
def x: String = "C"
}
Executing the snippet above in scala REPL results in the follwing error:
def x: String = "C"
^
<pastie>:10: error: `override` modifier required to override concrete member:
def x: String (defined in class A)
Now the question: Why it seems that the abstract method in class B was ignored? But B.x does actually have an effect if C.x is removed from definition. Because the following snippet doesn't compile either.
class A {
def x: String = "A"
}
abstract class B extends A {
def x: String
}
class C extends B
results in the following error
class C extends B
^
<pastie>:9: error: class C needs to be abstract. No implementation found in a subclass for deferred declaration
def x: String (defined in class B)
Second scenario
The concrete method to be overridden in super-class
The abstract method is in trait
class A {
def x: String = "A"
}
trait B {
def x: String
}
class C extends A with B
Try instantiate C,
scala> (new C).x
val res0: String = A
It looks like the B.x abstract method just got ignored by compiler.
Update
In the first edition of my question, I idiotically forgot to extends A in the second scenario, which leads to an incorrect conclusion that class and trait behave differently in my examples. I sincerely apologize for my negligence.
Let me try to rephase my question:
In both the first and second scenario, what is the effect of the abstract B.x in the middle of class hierarchy?
As I understand it, by inheritance and method resolution order (MRO),
in the first scenario B.x overrides A.x and C.x overrides B.x. Since B.x is abstract, when C.x implements B.x, it need not specify override modifier.
in the second scenario, B.x overrides A.x and C didn't implement the abstract B.x. So C should be abstract and does not compile.
But it seems to me that the compiler just ignored the abstract method B.x in the middle of class hierarchy. Is this behavior defined somewhere in the language specification, or this is totally unintended and unexpected (and just a compiler bug)?
I'm not sure if the question is about the differences between Traits and Abstract Classes or about the differences between extends.
In the First Scenario you have abstract class B extends A in both cases but in the Second Scenario you have trait A and trait B. B is not extending A.
In fact, if you put trait A extends B in the Second Scenario it won't compile
In the second case of the Second Scenario, you are extending 2 different traits that have the same method, having extends A with B your class has the method x defined because the order matters when you extend from multiple traits. If you try to do it in the opposite order extends B with A, it won't compile
In other words:
Second Scenario - First case: Defining an abstract method doesn't need the override
Second Scenario - Second case: C extends A with B is not the same as B extends A + C extends B

Scala - extends vs with

I am confused. In my existing project, I am not able to find the difference between extends and with. Could you please help me?
If you have multiple classes or traits to inherit, the first one is always extends, and the following >=0 class/trait to be withs.
But remember that you can only inherit <=1 (abstract) class, which means if you need to inherit a parent class (Parent), it should always comes at first of the form ... extends Parent ..., and no more classes can be inherited to the derived class.
trait T1
trait T2
class P1
class P2
class C1 extends T1
class C2 extends T1 with T2
class C3 extends T2 with T1
class C4 extends P1 with T1
/// class C5 extends T1 with P1 // invalid
/// class C6 extends P1 with P2 // invalid
with is in fact bound to the class/trait that is extended, e.g., class C7 extends P1 with T1 with T2 reads class C7 extends (P1 with T1 with T2).
Note that this is only from the viewpoint of syntax, the semantic differences can be referred from the followings:
use of trait and (abstract) class is
here;
The resolution rule is the so-called
class linearization; there is a post about it.
In Scala, you can inherit from classes (concrete or abstract) and traits. In a similar way to how you can extend only one class, but implement as many interfaces as you'd like in Java, you're only allowed to inherit from one class, but as many traits as you'd like.
If you are extending a class, that class should immediately follow the extends keyword. Any traits you are extending from should follow, separated by with keywords.
If you are not extending a class, you can put your first trait right after the extends keyword and continue after that.
Just as in Java, every class is a subclass of Object whether you explicitly declare it or not, every (user-defined) class in Scala extends AnyRef with ScalaObject whether or not you include that explicitly.
The following sets of declarations are therefore equivalent:
class MyClass { ... }
class MyClass extends AnyRef with ScalaObject { ... }
class MyClass extends MySuperClass { ... }
class MyClass extends MySuperClass with ScalaObject { ... }
class MyClass extends MyTrait1 with MyTrait2 { ... }
class MyClass extends AnyRef with MyTrait1 with MyTrait2 { ... }
class MyClass extends MySuperClass with MyTrait1 with MyTrait2 { ... }
The last example is not the same if you change the order of MySuperClass, MyTrait1, and MyTrait2. In particular, you cannot put a trait in front of a class and the order in which you stack traits is important if they both have implementations for the same methods. (In that case, the last one "wins".)
Remember also that only classes in Scala can have parameters, so you'll only ever be able to attach parameters to the type after the extends keyword, never to any of the types listed after with.
Hope that helps!
The first thing you inherit from can either be a trait or a class, using the extends keyword.
trait SomeTrait
class SomeClass
class ThisIsValid extends SomeTrait
class ThisAsWell extends SomeClass
You can define further inherited traits (and only traits) using the with keyword.
class AlsoThisWorks extends SomeClass with SomeTrait
If a trait inherits from a class you cannot use it like in the above example.
That's it regarding the extends and with keywords. If you want to learn more about classes and traits the official documentation goes in depth on the topic.

Stackable Traits Pattern : method's implementation "needs `abstract override' modifiers"

Recently, I've find out about the stackable trait pattern and followed the example described here. Everything works, but there is a case I cannot understand :
trait A {
def test : String
}
trait B extends A {
// 'abstract override' modifier required as
// the test() method is not yet implemented
abstract override def test = {
s"B${super.test}"
}
}
class C extends A with B {
// test method concrete implementation
override def test = { "C" }
}
<console>:10: error: overriding method test in trait B of type => String;
method test needs `abstract override' modifiers
class C extends A with B { override def test = { "C" } }
I cannot understand why this does not compile, and why the C::test method needs the mentioned modifier.
I've noticed that there is two modifications I can do in order to make this compile, either by composing the C class at runtime :
class C extends A { override def test = { "C" } }
new C with B // works as expected
or by adding an extra class (which is kind of the same but at compile time):
class C extends A {
override def test = { "C" }
}
class D extends C with B
new D().test
res5: String = BC
Why do I need an extra class (which BTW plays the role of the Basic class) ?
The reason for this behaviour is Scala's class linearization which is used to resolve ambiguities and the semantics of abstract override. But first things first.
Class Linearization
Whenever you have an instance a of type A and you call a method on it a.foobar(), the compiler has to figure out where to find the definition of foobar. Since A can extend any other class and a set of traits, there might be multiple definitions for the function foobar. In order to resolve these ambiguities, Scala will linearize your class A with all its superclasses and traits. The linearization will produce an order in which the different types are checked for a definition of foobar. The first match will be the function which is executed.
The Scala specification defines the linearization as following
Definition 5.1.2 Let C be a class with template C1 with ... with Cn { stats }.
The linearization of C, L(C) is defined as follows:
L(C) = C , L(Cn)+: ... +: L(C1)
Here +: denotes concatenation where elements of the right operand replace identical elements of the left operand.
Since all theory is grey, let's take a look at an example:
trait T1 {
def foobar() = 1
}
trait T2 {
def foobar() = 2
}
class B extends T2 {
override def foobar() = 42
}
class A extends B with T1 with T2 {
override def foobar() = super.foobar()
}
First of all, we have to override the foobar method in the class A, because we have multiple competing definitions for it. However, now is the question, which method definition is called by super.foobar. In order to find this out, we have to calculate the linearization of A.
L(A) = A, L(T2) +: L(T1) +: L(B)
L(B) = B, L(T2)
L(T2) = T2
L(T1) = T1
L(A) = A, T2 +: (T1, B, T2)
L(A) = A, T1, B, T2
Thus, super.foobar will call the definition in T1 which returns 1.
Abstract override
The abstract override modifier for a method basically says that there has to be a class/trait I implementing this method which appears after the trait with the abstract override modifier in the class linearization of your instantiated class. That is necessary in order to execute super.foobar(), because super.foobar() entails that the linearization is further searched for a definition of foobar.
When you now look at your definition of class C then you'll see that it has the following linearization
C, B, A
Consequently, it cannot compile, because beginning from B you don't find an implementation of test.
When we now look at the examples which work, then we'll why they actually work. In the case of C extends A with new C with B, you basically create an anonymous class Z extends C with B. The linearization of Z is
Z, B, C, A
There you see, that B can find in C an implementation of test. Thus, the code can compile. The same holds true for the example with class D.
According to the article you provided:
The base trait (or abstract class) defines an abstract interface that all the cores and stackables extend, as shown in Figure 1. The core traits (or classes) implement the abstract methods defined in the base trait, and provide basic, core functionality. Each stackable overrides one or more of the abstract methods defined in the base trait, using Scala's abstract override modifiers, and provides some behavior and at some point invokes the super implementation of the same method. In this manner, the stackables modify the behavior of whatever core they are mixed into.
In you case:
class C extends A with B { override def test = { "C" } }
you don't have core trait. A is base, as it defines the interface, B is stackable (as it calls super, expecting it to be implemented in core), C is also stackable, as the test declaration in the body of the class is the most concrete (it overrides one from all of the traits).
In your "fixed" examples you just introduced correct core implementation:
class C extends A { override def test = { "C" } }
new C with B // works as expected
class C extends A {
override def test = { "C" }
}
class D extends C with B
Here C defines test before it is overridden by B, so it serves as core.

Force Scala trait to implement a certain method

Is there a way to specify that a trait has to provide a concrete implementation of a method?
Given some mixin
class A extends B with C {
foo()
}
The program will compile if either of A, B, or C implements foo(). But how can we force, for example, B to contain foo's implementation?
You can do the following:
class A extends B with C {
super[B].foo()
}
This will only compile if B implements foo. Use with caution though as it (potentially) introduces some unintuitive coupling. Further, if A overrides foo, still B's foo will be called.
One IMHO valid use case is conflict resolution:
trait B { def foo() = println("B") }
trait C { def foo() = println("C") }
class A extends B with C {
override def foo() = super[B].foo()
}
If you want to make sure B declares foo, you can use type ascription:
class A extends B with C {
(this:B).foo()
}
This will only compile if B declares foo (but it might be implemented in C or A).

How to get around of invariance of MongoRecord in Scala

Say I have a class defined as follows:
class A extends MongoRecord[A]{
}
Now I need to create a new class B, that is a subclass of A:
class B extends A{
}
Object B extends B with MongoMetaRecord[B]
The compiler would give an error like:
type arguments [B] do not conform to trait MongoMetaRecord's type parameter bounds [BaseRecord <: net.liftweb.mongodb.record.MongoRecord[BaseRecord]]
[error] object B extends B with MongoMetaRecord[B]
It seems that the class B inherits MongoRecord[A], but because of the invariance of MongoRecord, MongoRecord[B] cannot substitute MongoRecord[A]. And therefore class B does not conform to the type constraint. Any idea how to get around this problem? Thanks a lot.
You will not be able to get around the invariance but instead of having B a subclass A perhaps make an ALike trait instead. You could turn A into a trait and create an ARecord.
trait ALike {
// all the common stuff
}
class A extends ALike with MongoRecord[A] {
}
class B extends ALike with MongoRecord[B] {
}
Actually the code won't probably compile because the compiler will complain that method "meta" and "id" needs to be implemented with the proper types but the idea of the solution is in the right direction.
class A extends ALike with MongoRecord[A] with MongoId[A] { def meta = A }
class B extends ALike with MongoRecord[B] with MongoId[B] { def meta = B }
object A extends A with MongoMetaRecord[A]
object B extends B with MongoMetaRecord[B]
It's a late response but maybe someone else will find it useful ;)
abstract class A[U <: A[U]] extends MongoRecord[U] with ... with ObjectIdPk[U] with ... {
self: U =>
object text extends StringField(this, 255)
... // other fields
}
class B extends A[B] {
override def meta = B
... // add more fields or methods
}
object B extends B with MongoMetaRecord[B] {
...
}
You can use ProtoAuthUser[U] in place of MongoRecord[U] or any other similar trait.