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).
Related
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.
I have seen this code is pretty common:
trait A { this: B => ... }
trait A { self: B => ... }
But I'm wondering, why not to use this:
trait A {
val self: B = this
//....
}
It this going to be an infinite recursion?
UPDATE:
Are these the same?
trait A { self => ... }
trait A { val self = this }
I think that in most useful cases your second version (using a field) simply won't compile.
trait A { self: B => ... }
What that snippet of code says is that any class that mixes in the A trait must also have type B. For example, if you are writing a trait that is only meant for use with your Logger class, then you can use trait X { self: Logger => ... } to communicate to the compiler that the X trait is only used as a mix-in for the Logger class.
In contrast, your alternative solution means something totally different:
trait A {
val self: B = this
//....
}
This code will compile if and only if B :> A (B is a supertype of A). I can't think of any situation where this would be useful. Even if you have nested classes where an outer this might be hidden, you can still use the Java-like OuterClassName.this syntax to get the this reference for an enclosing class.
I don't think there is any significant difference
you could always replace
trait A { bla: B => }; trait AA extends A with B
with
trait A { def bla: B }; trait AA extends A { val bla = new B }
of course AA is not an instance of B in this case, but that looks like a benefit of not leaking an implementation detail
Say I have two traits (A and B) and a class C which mixes them in and implements their methods:
trait A {
def foo
}
trait B {
def bar
}
class C extends A with B {
def foo = "Foo"
def bar = "Bar"
}
Is there any way in Scala to specify that the class that extends trait B must extend trait A and then use the implemented method of trait A's defined method in trait B?
So that B could call this.foo() and access the value that has been returned by C's implementation?
Just specify what you want this to be:
trait B { this: A =>
def bar = this.foo
}
This is so called self type and this here is an alias rather than keyword (so self: A, that: A and so on are perfectly legal).
I'm trying define a trait C which extends some traits A,B,... All traits,C and A,B,... implement a common trait T. Trait C is supposed to implement T by calling the implementations of T in A,B,..:
trait T{
def f()
}
trait A extends T{
def f(){
print("A")
}
}
trait B extends T{
def f(){
print("B")
}
}
The desired behavior of trait C is as follows:
val x=new A with B with C[A,B]{}
x.f()
// should produce output
A
B
Here my attempt to define trait C, which gave compilation errors:
trait C[A<:T,B<:T] extends T{
self:A with B =>
override def f(){
// error: A does not name a parent class of trait C
super[A].f()
// error: B does not name a parent class of trait C
super[B].f()
}
}
I need to call within C methods A.f() and B.f().
Is there any solution to this?
If you want to provide an implementation inside of a trait but also ensure that subclasses implement the definition, it is possible to tell this the compiler with the abstract override combination:
trait T {
def f()
}
trait A extends T {
abstract override def f() {
super.f()
print("A")
}
}
trait B extends T {
abstract override def f() {
super.f()
print("B")
}
}
trait C extends T {
override def f() {
// do your work here ...
}
}
val x = new C with A with B
x.f()
To call the next implementation in the mixin-hierarchy you must add a super.f() call inside of the abstract override method call. Because such a super call requires an existing implementation the first thing you need to create is an instance of C that mixins A and B. If you mixin C in A or B the compiler will complain because the mixin-hierarchy is executed from left to right, thus the implementation of C can not be seen.
I have a base class that comes from a Java library, whose code I cannot modify. This class (A) has an empty method (b) which should have been declared as abstract instead:
class A {
def b { }
}
I extend this class in Scala and override the method to make it abstract:
abstract class AA extends A {
override def b
}
Now I implement this method in a trait:
trait B {
def b { println("B") }
}
If I extend AA with trait B, I get a error: overriding method b in class A of type => Unit;
method b in trait B of type => Unit needs `override' modifier:
class C extends AA with B {}
Instead, if the code had been like this, everything is compiled without errors, which seems a bit contradictory to me:
abstract class AA {
def b
}
trait B {
def b { println("B") }
}
class C extends AA with B {}
I'm running Scala 2.8.0RC3, and completely new to the language (3 days). Another weird and related behaviour is that the override label is not necessary when making b abstract:
abstract class AA extends A {
def b
}
To try to see what's going on, I tried this:
scala> class A{
| def b{ }
| }
defined class A
scala> abstract class AA extends A{
| override def b
| }
defined class AA
scala> class AAA extends AA{
| def b = println("AAA")
| }
<console>:8: error: overriding method b in class A of type => Unit;
method b needs `override' modifier
def b = println("AAA")
^
Apparently, the source of the problem is that abstract classes can't "free up" methods in their superclass from the need for the abstract class's subclasses to include the 'override' modifier.
Not sure if this is the right solution, but if your trait B extends A (and override b), then everything compile fine:
First let's define A and AA like you present them in your question:
C:\Users\VonC>scala
Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.
scala> class A {
| def b { println("A b") }
| }
defined class A
scala> new A
res5: A = A#153bedc4
scala> res5.b
A b
scala> abstract class AA extends A {
| override def b
| }
defined class AA
What you did:
scala> trait B {
| override def b { println("B b") }
| }
<console>:6: error: method b overrides nothing
override def b { println("B b") }
^
What I tried with trait B (in order to be able to add the 'override'):
scala> trait B extends A {
| override def b { println("B b") }
| }
defined trait B
So now:
scala> class C extends AA with B {}
defined class C
scala> new C
res7: C = C#1497b7b1
scala> res7.b
B b
The right b overriden method is called with C.b
As for your apparent "inconsistency", see Scala for Java Refugees Part 5: Traits and Types:
To start with, there’s that ever-annoying override keyword. I mentioned back in the article on basic OOP that any method which overrides a method in a superclass must be declared with the override modifier. At the time, I likened it to the language mandating the use of the #Override annotation, with its primary purpose being to enforce the good practice.
The real key to the power of traits is the way in which the compiler treats them in an inheriting class.
Traits are actually mixins, not true parent classes.
Any non-abstract trait members are actually included in the inheriting class, as in physically part of the class. Well, not physically, but you get the picture.
It’s as if the compiler performs a cut-and-paste with the non-abstract members and inserts them into the inheriting class. This means that there’s no ambiguity in the inheritance path, meaning no diamond problem.
So no need for override keyword in your second example.
The problem is very subtle. As a rule of thumb, your class AA, which extends A, should be mixed with traits that also extend A.
You did:
class A {
def b { }
}
abstract class AA extends A {
override def b
}
trait B {
def b { println("B") }
}
Hence when you mix AA and B method b is DEFINED twice. Once by A (Not overrided because the definition in B replaced the override in AA) and the second by B, the compiler can't chose one over the other because there is no hierarchy between the two (equaly named but unrelated) methods. If you want, think about it like this: The compiler "mixes" the bodies of AA and B; if he choses the method from AA it will be abstract, if he choses the method from B (What should happend), since it's not an override, your stuck with two methods b.
To solve this, you want to make sure that both methods override the same method, in which case the the compiler will understand you are talking about the SAME method and will give priority to the last trait mixed.
Now, to override the method b in B, that class has also to inherit from A. So the canonical way to do this would be:
class A {
def b { }
}
abstract class AA extends A {
override def b
}
trait B extends A{
def b { println("B") }
}
class C extends AA with B {}
Which compiles just fine.
Now, when you do:
abstract class AA {
def b
}
trait B {
def b { println("B") }
}
class C extends AA with B {}
it's clear that both methods are the same so the compiler knows he has to use the method from the trait.
Other solutions include:
Make B override AA
Make b in A abstract (But you didn't want that)
Again, the problem is very subtle but I hope I made it a little clearer. To get a better understandig read Scala's Stackable Trait Pattern.