A self-type for a trait A
:
trait B
trait A { this: B => }
says that "A
cannot be mixed into a concrete class that does not also extend B
".
On the other hand, the following:
trait B
trait A extends B
says that "any (concrete or abstract) class mixing in A
will also be mixing in B".
Don't these two statements mean the same thing? The self-type seems to serve only to create the possibility of a simple compile-time error.
What am I missing?
trait A[Self] {this: Self => }
is legal, trait A[Self] extends Self
isn't.
It is predominately used for Dependency Injection, such as in the Cake Pattern. There exists a great article covering many different forms of dependency injection in Scala, including the Cake Pattern. If you Google "Cake Pattern and Scala", you'll get many links, including presentations and videos. For now, here is a link to another question.
Now, as to what is the difference between a self type and extending a trait, that is simple. If you say B extends A
, then B
is an A
. When you use self-types, B
requires an A
. There are two specific requirements that are created with self-types:
If B is extended, then you're required to mix-in an A. When a concrete class finally extends/mixes-in these traits, some class/trait must implement A.
Consider the following examples:
scala> trait User { def name: String }
defined trait User
scala> trait Tweeter {
| user: User =>
| def tweet(msg: String) = println(s"$name: $msg")
| }
defined trait Tweeter
scala> trait Wrong extends Tweeter {
| def noCanDo = name
| }
<console>:9: error: illegal inheritance;
self-type Wrong does not conform to Tweeter's selftype Tweeter with User
trait Wrong extends Tweeter {
^
<console>:10: error: not found: value name
def noCanDo = name
^
If Tweeter
was a subclass of User
, there would be no error. In the code above, we required a User
whenever Tweeter
is used, however a User
wasn't provided to Wrong
, so we got an error. Now, with the code above still in scope, consider:
scala> trait DummyUser extends User {
| override def name: String = "foo"
| }
defined trait DummyUser
scala> trait Right extends Tweeter with User {
| val canDo = name
| }
defined trait Right
scala> trait RightAgain extends Tweeter with DummyUser {
| val canDo = name
| }
defined trait RightAgain
With Right
, the requirement to mix-in a User
is satisfied. However, the second requirement mentioned above is not satisfied: the burden of implementing User
still remains for classes/traits which extend Right
.
With RightAgain
both requirements are satisfied. A User
and an implementation of User
are provided.
For more practical use cases, please see the links at the start of this answer! But, hopefully now you get it.
Self types allow you to define cyclical dependencies. For example, you can achieve this:
trait A { self: B => }
trait B { self: A => }
Inheritance using extends
does not allow that. Try:
trait A extends B
trait B extends A
error: illegal cyclic reference involving trait A
In the Odersky book, look at section 33.5 (Creating spreadsheet UI chapter) where it mentions:
In the spreadsheet example, class Model inherits from Evaluator and thus gains access to its evaluation method. To go the other way, class Evaluator defines its self type to be Model, like this:
package org.stairwaybook.scells
trait Evaluator { this: Model => ...
Hope this helps.
One additional difference is that self-types can specify non-class types. For instance
trait Foo{
this: { def close:Unit} =>
...
}
The self type here is a structural type. The effect is to say that anything that mixes in Foo must implement a no-arg "close" method returning unit. This allows for safe mixins for duck-typing.
abstract class A extends {def close:Unit}
is equivalent to abstract class A {def close:Unit}
. So it does not involve structural types.
Another thing that has not been mentioned: because self-types aren't part of the hierarchy of the required class they can be excluded from pattern matching, especially when you are exhaustively matching against a sealed hierarchy. This is convenient when you want to model orthogonal behaviors such as:
sealed trait Person
trait Student extends Person
trait Teacher extends Person
trait Adult { this : Person => } // orthogonal to its condition
val p : Person = new Student {}
p match {
case s : Student => println("a student")
case t : Teacher => println("a teacher")
} // that's it we're exhaustive
Section 2.3 "Selftype Annotations" of Martin Odersky's original Scala paper Scalable Component Abstractions actually explains the purpose of selftype beyond mixin composition very well: provide an alternative way of associating a class with an abstract type.
The example given in the paper was like the following, and it doesn't seem to have an elegant subclass correspondent:
abstract class Graph {
type Node <: BaseNode;
class BaseNode {
self: Node =>
def connectWith(n: Node): Edge =
new Edge(self, n);
}
class Edge(from: Node, to: Node) {
def source() = from;
def target() = to;
}
}
class LabeledGraph extends Graph {
class Node(label: String) extends BaseNode {
def getLabel: String = label;
def self: Node = this;
}
}
TL;DR summary of the other answers:
Types you extend are exposed to inherited types, but self-types are not eg: class Cow { this: FourStomachs } allows you to use methods only available to ruminants, such as digestGrass. Traits that extend Cow however will have no such privileges. On the other hand, class Cow extends FourStomachs will expose digestGrass to anyone who extends Cow .
self-types allow cyclical dependencies, extending other types does not
Let's start with the cyclical dependency.
trait A {
selfA: B =>
def fa: Int }
trait B {
selfB: A =>
def fb: String }
However, the modularity of this solution is not as great as it might first appear, because you can override self types as so:
trait A1 extends A {
selfA1: B =>
override def fb = "B's String" }
trait B1 extends B {
selfB1: A =>
override def fa = "A's String" }
val myObj = new A1 with B1
Although, if you override a member of a self type, you lose access to the original member, which can still be accessed through super using inheritance. So what is really gained over using inheritance is:
trait AB {
def fa: String
def fb: String }
trait A1 extends AB
{ override def fa = "A's String" }
trait B1 extends AB
{ override def fb = "B's String" }
val myObj = new A1 with B1
Now I can't claim to understand all the subtleties of the cake pattern, but it strikes me that the main method of enforcing modularity is through composition rather than inheritance or self types.
The inheritance version is shorter, but the main reason I prefer inheritance over self types is that I find it much more tricky to get the initialisation order correct with self types. However, there are some things you can do with self types that you can't do with inheritance. Self types can use a type while inheritance requires a trait or a class as in:
trait Outer
{ type T1 }
trait S1
{ selfS1: Outer#T1 => } //Not possible with inheritance.
You can even do:
trait TypeBuster
{ this: Int with String => }
Although you'll never be able to instantiate it. I don't see any absolute reason for not being be able to inherit from a type, but I certainly feel it would be useful to have path constructor classes and traits as we have type constructor traits / classes. As unfortunately
trait InnerA extends Outer#Inner //Doesn't compile
We have this:
trait Outer
{ trait Inner }
trait OuterA extends Outer
{ trait InnerA extends Inner }
trait OuterB extends Outer
{ trait InnerB extends Inner }
trait OuterFinal extends OuterA with OuterB
{ val myV = new InnerA with InnerB }
Or this:
trait Outer
{ trait Inner }
trait InnerA
{this: Outer#Inner =>}
trait InnerB
{this: Outer#Inner =>}
trait OuterFinal extends Outer
{ val myVal = new InnerA with InnerB with Inner }
One point that should be empathised more is that traits can extends classes. Thanks to David Maclver for pointing this out. Here's an example from my own code:
class ScnBase extends Frame
abstract class ScnVista[GT <: GeomBase[_ <: TypesD]](geomRI: GT) extends ScnBase with DescripHolder[GT] )
{ val geomR = geomRI }
trait EditScn[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
trait ScnVistaCyl[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
ScnBase
inherits from the Swing Frame class, so it could be used as a self type and then mixed in at the end (at instantiation). However, val geomR
needs to be initialised before it's used by inheriting traits. So we need a class to enforce prior initialisation of geomR
. The class ScnVista
can then be inherited from by multiple orthogonal traits which can themselves be inherited from. Using multiple type parameters (generics) offers an alternative form of modularity.
trait A { def x = 1 }
trait B extends A { override def x = super.x * 5 }
trait C1 extends B { override def x = 2 }
trait C2 extends A { this: B => override def x = 2}
// 1.
println((new C1 with B).x) // 2
println((new C2 with B).x) // 10
// 2.
trait X {
type SomeA <: A
trait Inner1 { this: SomeA => } // compiles ok
trait Inner2 extends SomeA {} // doesn't compile
}
A self type lets you specify what types are allowed to mixin a trait. For example, if you have a trait with a self type Closeable
, then that trait knows that the only things that are allowed to mix it in, must implement the Closeable
interface.
trait A { self:B => ... }
then a declaration X with A
is only valid if X extends B. Yes, you can say X with A with Q
, where Q does not extend B, but I believe kikibobo's point was that X is so constrained. Or did I miss something?
Update: A principal difference is that self-types can depend on multiple classes (I admit that's a bit corner case). For example, you can have
class Person {
//...
def name: String = "...";
}
class Expense {
def cost: Int = 123;
}
trait Employee {
this: Person with Expense =>
// ...
def roomNo: Int;
def officeLabel: String = name + "/" + roomNo;
}
This allows to add the Employee
mixin just to anything that is a subclass of Person
and Expense
. Of course, this is only meaningful if Expense
extends Person
or vice versa. The point is that using self-types Employee
can be independent of the hierarchy of the classes it depends on. It doesn't care of what extends what - If you switch the hierarchy of Expense
vs Person
, you don't have to modify Employee
.
in the first case, a sub-trait or sub-class of B can be mixed in to whatever uses A. So B can be an abstract trait.
Success story sharing
trait WarmerComponentImpl extends SensorDeviceComponent with OnOffDeviceComponent
? That would causeWarmerComponentImpl
to have those interfaces. They would be available to anything that extendedWarmerComponentImpl
, which is clearly wrong, as it is not aSensorDeviceComponent
, nor aOnOffDeviceComponent
. As a self type, these dependencies are available exclusively toWarmerComponentImpl
. AList
could be used as anArray
, and vice versa. But they just aren't the same thing.this
with self types is something I look down upon, since it shadows for no good reason the originalthis
.self: Dep1 with Dep2 =>
.