The Programming in Scala 2d edition introduce the concept of self type in the section 29.4 "Splitting modules into trait":
The SimpleFoods trait could look as:
trait SimpleFoods {
object Pear extends Food("Pear")
def allFoods = List(Apple, Pear)
def allCategories = Nil
}
So far so good, but unfortunately, a problem arises if you try to define a SimpleRecipes trait like this:
trait SimpleRecipes { // Does not compile
object FruitSalad extends Recipe(
"fruit salad",
List(Apple, Pear), // Uh oh
"Mix it all together."
)
def allRecipes = List(FruitSalad)
}
The problem here is that Pear
is located in a different trait from the one that
uses it, so it is out of scope.
The compiler has no idea that SimpleRecipes
is only ever mixed together with SimpleFoods
.
There is a way you can tell this to the compiler, however. Scala provides the self type for precisely this situation.
Technically, a self type is an assumed type for this whenever this is mentioned within the class.
Pragmatically, a self type specifies the requirements on any concrete class the trait is mixed into.
If you have a trait that is only ever used when mixed in with another trait or traits, then you can specify that those other traits should be assumed.
In the present case, it is enough to specify a self type of SimpleFoods
, as shown:
trait SimpleRecipes {
this: SimpleFoods =>
object FruitSalad extends Recipe(
"fruit salad",
List(Apple, Pear), // Now Pear is in scope
"Mix it all together."
)
def allRecipes = List(FruitSalad)
}
Given the new self type, Pear
is now available.
Implicitly, the reference to Pear
is thought of as this.Pear
.
This is safe, because any concrete class that mixes in SimpleRecipes
must also be a subtype of SimpleFoods
, which means that Pear
will be a member.
Abstract subclasses and traits do not have to follow this restriction, but since they cannot be instantiated with new, there is no risk that the this.Pear
reference will fail