Published 2014-02-08.
Last modified 2016-10-11.
Time to read: 6 minutes.
This lecture discusses the use of self types with traits to implement a requires-a relationship between types, and gives some example of how self types can be used. Self types allow a dependency to be expressed within a trait, that when satisfied allows the trait to make use of code defined in another type – without polluting the trait with the implementation details of the other type.
This lecture compares self types with both composition (has-a relations) and inheritance (is-a relations). It includes a comprehensive example of using self types, inheritance and composition in object-oriented modeling.
In the previous lectures, when we defined a trait, its been totally independent of any class we might mix it in with.
But what if we want our trait to make use of functionality defined in the type its being mixed in with – either a class or a trait? In the
following code we have defined a Person
class that can speak.
class Person(name: String) { def speak(feelings: String) = println(feelings) }
Self Types

Now suppose we want provide emotion-related traits that use the Person
’s speak
method, that we can mix in with the
Person
class, to provide a mode of expression – happy, sad, angry and so on.
For example, in the following code we define an Angry
trait.
trait Angry { self: Person => def growl = self.speak("I’m having a bad day!!") }
Note the annotation self: Person =>
.
This is called a self type.
It does two things.
- It restricts the trait to being used with the
Person
class, or a sub-class ofPerson
. For example, we can’t mix in theAngry
trait with aBird
class to create anAngry Bird
. Self types declare the dependency of the trait on another type, which means, in effect: "In order to use me, you have to be one of those". Another way of looking at it is that theAngry
trait requires thePerson
class. - It allows the trait to access the methods and properties of the required type through the
self
variable, which explicitly declares the type of the variable and allows it to be used.
The annotation essentially makes the named type (in this case Person
) the this
of the trait.
Some additional notes.
Using the variable name self
for a self type is a common convention, but you can use any name for the variable, It’s also very
common to see self types defined using this
, for example.
trait Whatever { this: Person => /* body of trait goes here */ }
The use of the variable (self
or this
) when accessing the self type, is optional, so the following will work just fine.
trait Angry { self: Person => def growl = speak("I’m having a bad day!!") }
While self types are most frequently used with traits, they can also be used with classes.
Self types allow a dependency to be expressed within a trait that, when satisfied, allows the trait to make use of the code defined in another type – without polluting the trait with the implementation details of the other type. That other type could be another trait, a class, or a structural type, which we will learn about in the Structural Types lecture of the Intermediate Scala course. The concrete class subclassed from the self-typed trait must also subclass the self type, of course.
Scala Self-Types by Gregor Heine provides more information on object-oriented modeling using self types.
Exercise - Self Types
Given the following class that models how people express their feelings.
class Person(name: String) { def speak(feelings: String) = println(feelings) }We also have a trait that provides a mode of expression if the
Person
instance is angry.
This trait adds a method called growl
to the Person
when mixed in:
trait Angry { self: Person => def growl = self.speak("I’m having a bad day!!") }

- Mix the
Angry
trait into thePerson
case class, and get an instance of thePerson
class togrowl
. - Define a
Happy
trait and get thePerson
instance tolaugh
.
 .
Solution
These solutions are provided in courseNotes/src/main/scala/solutions/Emotions.scala
.
package solutions object Emotions extends App { class Person(val name: String) { def speak(feelings: String): Unit = println(feelings) } trait Angry { self: Person => def growl = self.speak(s"$name is having a bad day") } trait Happy { self: Person => def laugh = self.speak(s"$name is happy") } // Solution 1: (new Person("Fred Flintstone") with Angry).growl (new Person("Wilma Flintstone") with Angry with Happy).laugh // Solution 2: class AngryPerson(override val name: String) extends Person(name) with Angry class HappyPerson(override val name: String) extends Person(name) with Happy new AngryPerson("Bambam Rubble").growl new HappyPerson("Pebbles Flintstone").laugh }
You can run this by typing.
$ scala> sbt "runMain solutions.Emotions"
Self Types vs Inheritance vs Composition
Self types add another tool in object-oriented modeling.
- Inheritance: The is a relationship.
When you say
B
extends A
, thenB
is anA
- Composition: The has a relationship.
When you put a
B
inside anA
by defining aval
or avar
, thenA
hasB
(orB
is contained in, or is part ofA
) - Requires: requires a relationship.
When you put
self: A =>
in typeB
thenB
requiresA
Note that If trait A
extends B
, then mixing in A
gives you precisely B
plus whatever
A
adds or extends.
In contrast, if trait A
has a self-reference which is explicitly typed as B
, then the ultimate parent class must also mix
in B
or a subtype of B
(and mix it in first, which is important).
In the first case, the precise type of B
is crystallized at the point A
extends it.
In the second, the designer of the subtype gets to decide which version of B
is used, at the point where the subtype is composed.
Self Types and Composition
The example code for this section can be found at:
To compare self types and composition, let’s build a bicycle.
We’ll also use some other things we’ve learned about object oriented programming with Scala.
We are going to calculate gear ratios and speed, so our bike is composed of a
courseNotes/src/main/scala/Bicycle.scala
ChainRing
(the front gear), and a Wheel
(the rear wheel).
Here’s what our Bike
class looks like.
/** A bike has a chainring and rear wheel components */ case class Bike(chainRing: ChainRing, wheel: Wheel) { def gearRatio(gear: Int): Double = chainRing.frontTeeth.toDouble / wheel.gearTeeth(gear).toDouble def distancePerRevolution(gear: Int): Double = gearRatio(gear) * wheel.turn(1) /** @param rpm revolutions per minute that the cyclist pedals * @param gear gear number that is currently engaged */ def rpmToKph(rpm: Int=90, gear: Int=1): Double = distancePerRevolution(gear) * rpm * 60.0 / 1000.0 val numGears: Int = wheel.numGears }

Bike
has a ChainRing
and a Wheel
.
Now let’s define the class ChainRing
and a single concrete subclass of a ChainRing
with 50 teeth.
/** Assume a single front chainring for simplicity */ trait ChainRing { def frontTeeth: Int } class ChainRing50 extends ChainRing { val frontTeeth = 50 } class ChainRing48 extends ChainRing { val frontTeeth = 48 }
Next we define the Wheel
class.
Here we use self types and a requires relationship.
A Wheel
requires a gear Cassette
and a Tire
with a specific size.
In the example below we also create two different concrete wheels, and TrainingWheel
and a RacingWheel
.
/** A wheel requires a tire and a cassette */ trait Wheel { self: Tire with Cassette => /** @param numTurns a number of turns of the bicycle wheel as a real number * @return the total distance travelled for the given number of turns, in meters */ def turn(numTurns: Double): Double = self.tireSize * numTurns /** @param gear is the gear number, with the lowest number being the outside gear * @return the number of teeth of the given gear */ def gearTeeth(gear: Int): Int = if (gear > rearTeeth.size) rearTeeth.last else if (gear < 1) rearTeeth.head else rearTeeth(gear - 1) def numGears = self.rearTeeth.size } class TrainingWheel extends Wheel with TrainingTire with TouringCassette class RacingWheel extends Wheel with RacingTire with RacingCassette

Tire
trait, and a Cassette
trait that we can mix in to a Wheel
to make
different types of wheels.
We need a collection of Int
to hold the number of rear teeth on each gear of the cassette.
Because we will be performing random accesses into this immutable collection, we use the Vector
collection type instead of
List
or Array
.
The Immutable Collections lecture of the
Intermediate Scala course explains
Vector
in more detail; that lecture is one of five lectures in that course dedicated to collections.
Scala collections are parametric, and the
Parametric Types lecture of the
Intermediate Scala course discusses parametric types, also known as generic programming.
For now, we’ll just recognize that the rearTeeth
variable is an instance of a Vector
collection of Int
.
trait Cassette { /** The number of cogs on each gear in the cassette */ protected def rearTeeth: Vector[Int] } trait TouringCassette extends Cassette { protected val rearTeeth = Vector(14, 16, 18, 20, 22, 24, 26) } trait RacingCassette extends Cassette { protected val rearTeeth = Vector(11, 12, 13, 14, 15, 17, 19, 21, 23, 25) } trait Tire { /** Circumference is measured in meters */ def tireSize: Double } trait RacingTire extends Tire { val tireSize = 2.096 } trait TrainingTire extends Tire { val tireSize = 2.196 }

val racingBike = Bike(new ChainRing50, new RacingWheel) println(f"racingBike.distancePerRevolution(1) = ${racingBike.distancePerRevolution(1)}%2.1f meters") println(f"racingBike.rpmToKph(95, 1) = ${racingBike.rpmToKph(95, 1)}%2.1f KpH") val touringBike = Bike(new ChainRing48, new TrainingWheel) println(f"touringBike.distancePerRevolution(5) = ${touringBike.distancePerRevolution(5)}%2.1f meters") println(f"touringBike.rpmToKph(95, 5) = ${touringBike.rpmToKph(95, 5)}%2.1f KpH") // Calculate speeds for each gear on the touring bike println("When riding a Touring Bike at 95 RPM, the speeds for each gear are:") 1 to touringBike.numGears foreach { gear => println(f"Gear $gear: Speed is ${touringBike.rpmToKph(95, gear)}%2.1f KpH") }
You can run this by typing:

$ scala> sbt "runMain Bicycle"
Output is.
racingBike.distancePerRevolution(1) = 9.5 meters racingBike.rpmToKph(95, 1) = 54.3 KpH touringBike.distancePerRevolution(5) = 4.8 meters touringBike.rpmToKph(95, 5) = 27.3 KpH When riding a Touring Bike at 95 RPM, the speeds for each gear are: Gear 1: Speed is 42.9 KpH Gear 2: Speed is 37.6 KpH Gear 3: Speed is 33.4 KpH Gear 4: Speed is 30.0 KpH Gear 5: Speed is 27.3 KpH Gear 6: Speed is 25.0 KpH Gear 7: Speed is 23.1 KpH
Formatted String Interpolation
Back in the Learning Scala Using the REPL
lecture we introduced Scala string interpolations using the s
string interpolation.
You will note in the above example we’ve used the formatted string interpolation, or f
interpolation, to format the output.
To recap, a string of the form s"Some text ${myExpression}"
will have the value of the expression myExpression
converted to a string and inserted in place of ${myExpression}
.
Each variable will automatically be converted to a string by calling its toString
method, and expressions within { curly braces } are
evaluated and the result’s toString
method is called.
The curly braces are optional for simple variable references, but are required when referencing properties or classes of objects.
Formatted string interpolation are similar to simple string interpolation, except it adds printf
-style formatting and uses the
f
prefix.
Scala uses Java’s Formatter
class for f
string interpolation formatting.
The java.util.Formatter
documentation describes the available formatting directives.
The references to expressions use the same s"Some text ${myExpression}"
format as before, however the printf
formatting directives are appended to the end of the expression with no spaces.
Here are some examples of formatting directives.
object FormattedStringSamples extends App { val int = 123 val double = 123.4567 val string = "Hello" println(f"Signed Integer right-justified at least 6 wide |$int%6d|") println(f"Signed Integer left-justified at least 6 wide |$int%-6d|") println(f"Integer right-justified at least 6 wide, zero filled |$int%06d|") println(f"Integer right-justified at least 6 wide, leading + |$int%+6d|") println(f"Floating point right-justified at least 10 wide, 2 positions after the decimal |$double%10.2f|") println(f"Floating point left-justified at least 10 wide, 2 positions after the decimal |$double%-10.2f|") println(f"Floating point scientific notation right-justified at least 10 wide, 2 positions after the decimal |$double%10.2E|") println(f"Floating point scientific notation left-justified at least 10 wide, 2 positions after the decimal |$double%-10.2E|") println(f"String minimum length 10, right-justified |$string%10s|") println(f"String minimum length 10, left-justified |$string%-10s|") println(f"Computation minimum length 10, right-justified |${int * double}%-10.2f|") println(f"Computation minimum length 10, left-justified |${int * double}%10.2f|") }
You can run this by typing.
$ scala> sbt "runMain FormattedStringSamples"
Output is.
Signed Integer right-justified at least 6 wide | 123| Signed Integer left-justified at least 6 wide |123 | Integer right-justified at least 6 wide, zero filled |000123| Integer right-justified at least 6 wide, leading + | +123| Floating point right-justified at least 10 wide, 2 positions after the decimal | 123.46| Floating point left-justified at least 10 wide, 2 positions after the decimal |123.46 | Floating point scientific notation right-justified at least 10 wide, 2 positions after the decimal | 1.23E+02| Floating point scientific notation left-justified at least 10 wide, 2 positions after the decimal |1.23E+02 | String minimum length 10, right-justified | Hello| String minimum length 10, left-justified |Hello | Computation minimum length 10, right-justified |15185.17 | Computation minimum length 10, left-justified | 15185.17|
Self Types and Inheritance
The code in this section is provided in Traits2.scala
.
To see the difference between a self type and subclassing a trait, let’s define a User
trait, then use it twice, once with a
Tweeter
trait that subclasses User
and once with a Tweeter2
trait that uses self typing to merely require
User
.
trait User { def name: String }
trait Tweeter extends User {
def tweet(msg: String): Unit = println(s"$name: $msg")
}
trait Tweeter2 { self: User =>
def tweet(msg: String): Unit = println(s"${self.name}: $msg")
def tweet2(msg: String): Unit = println(s"$name: $msg")
}
class Blabber(val name: String) extends Tweeter
class Blabber2(override val name: String) extends Tweeter2 with User
Some observations about this code.
-
The
User
trait defined thename
property as adef
, in accordance with the uniform access principle, which we will discuss in the Setters, Getters and the Uniform Access Principle lecture. - The
Tweeter
trait extendsUser
, so aTweeter
is aUser
because aTweeter
is aUser
subclass. This means that theUser
implementation is completely visible withinTweeter
. Tweeter
does not defineUser.name
, so any concrete subclass of theTweeter
trait must fulfill theUser
contract by definingname
.- The
Tweeter2
trait does not extendUser
, so theTweeter2
trait does not fulfill theUser
contract. In other words, aTweeter2
is not aUser
, however it requires that the object which provides the concrete implementation ofTweeter2
must be aUser
. This means that ifUser
is a pure trait or a Java interface, the implementation details ofUser
are not exposed in theTweeter2
trait. - The self type instance’s name is
self
, which is a common convention. Feel free to use any arbitrary name in your code if you prefer. Blabber
’sname
parameter to the class constructor is also an immutable public property, so it fulfills the contract of theUser
trait, in that aname
property must be defined.- Both the
Blabber.name
and theBlabber2.name
parametersimplementUser.name
, and they do not need to be markedoverride
. Whether you write the wordoverride
or not is a matter of personal style.
Now let’s try it out.
scala> val blabber = new Blabber("Mr. Itoktumuch") blabber: Blabber = Blabber@57cd2786
scala> %}blabber.tweet("tweet tweet tweet") Mr. Itoktumuch: tweet tweet tweet
scala> %}val blabber2 = new Blabber2("Ms. Nufsaid") blabber2: Blabber2 = Blabber2@4da5181a
scala> %}blabber2.tweet2("Le tweet") Ms. Nufsaid:Le tweet
scala> %}blabber2.tweet("Une autre tweet") Ms. Nufsaid: Une autre tweet
You can run this code by typing.
$ scala> sbt "runMain Tweeters"
Mr.
Itoktumuch: tweet tweet tweet
Ms.
Nufsaid: Le tweet
Ms.
Nufsaid: Une autre tweet
Using Scala’s Singleton as a Self Type
Scala has an little-known Singleton
type, which can be used to enforce singletons.
If you have an aversion to the singleton pattern, read no further.
Although Singleton
does not appear in the Scaladoc, it is mentioned in the Scala Language Specification, §3.2.1.
One way to use Singleton
is to define a trait that can only be subclassed by singletons.
trait Lonely { self: Singleton => override def toString = "I am so shy!" } object OneAndOnly extends Lonely println(OneAndOnly.toString)
The compiler won’t let you define a class that extends the Lonely
trait.
class Oops extends Lonely
This is the error message.
Error: illegal inheritance; self-type Oops does not conform to Lonely’s selftype Lonely with Singleton
You can run this code by typing.
$ scala> sbt "runMain Wallflower"
© Copyright 1994-2024 Michael Slinn. All rights reserved.
If you would like to request to use this copyright-protected work in any manner,
please send an email.
This website was made using Jekyll and Mike Slinn’s Jekyll Plugins.