http://image.motortrend.com/f/features/consumer/1301_chevrolet_corvette_60_years_american_icon_part_1/42023018/1961-Chevro...
Lets build a car...
Components
●
●
●
●
●
●
●
●

Engine
Fuel Tank
Wheels
Gearbox
Steering
Accelerator
Clutch
etc...
OK, let's write some interfaces
trait Engine {
def start(): Unit
def stop(): Unit
def isRunning(): Boolean
def fuelType: FuelType
}
trait Car {
def drive(...
Well actually, in Scala we can also
add some implementation
trait Engine {
private var running = false
def start(): Unit = {
if (!running) println("Engine started")
running = true
}
...
Fine, so how do we put an
Engine into a Car?
First attempt:
Inheritance-based assembly
trait Car extends Engine {
def drive() {
start()
println("Vroom vroom")
}
def park() {
if (isRunning() ) println("Break!")...
Hmm, this isn't great:
A Car is also an Engine
:(
This should really be a has-a relation,
right?
Second attempt:
Composition-based assembly
trait Car {
def engine : Engine
def drive() {
engine.start()
println("Vroom vroom")
}
def park() {
if (engine.isRunning() ...
Hmm OK, a Car has an Engine.
That's better.
But...
There is no guarantee that the
Engine in myCar isn't used in
another Car!
:(
If only there was a way to "mix-in" an
Engine into my car rather than supplying
it from the outside
Enter: self-types
“A self type of a trait is the assumed type of this,
the receiver, to be used within the trait. Any
concrete class that mi...
Erm, what, what, what?
Fine, let's look at an example:
trait Car {
this: Engine => // self-type
def drive() {
start()
println("Vroom vroom")
}
def park() {
println("Break!")
sto...
trait Car {
this: Engine => // self-type
def drive() {

Looks a bit like composition-based assembly

start()
println("Vroo...
So what's happening here?
● Self-types are used with traits
● Explicitly declare the type of the value this
● Specify the requirements on any concre...
So, tell me more about self-types
No need to call the self-type "this"
You can use this-aliasing to give it a
different name:
trait Car {
engine: Engine =>
def drive() {
engine.start()
println("Vroom vroom")
}
}

Useful for nested classes or traits...
Self-types don’t automatically inherit:
trait HondaCar extends Car
// error: self-type HondaCar does not conform to Car's ...
A self-type can require multiple types:
trait Car {
this: Engine with FuelTank with GearBox =>
// ...
}

Used when the tra...
The self-type can be a structural type:
trait Car {
this: {
def start: Unit
def stop: Unit
} =>
// ...
}

Allows for safe ...
So all is good with self-types?
Hmm, well no:
val myCar = new Car extends DieselEngine {}

myCar is still a Car and an Engine
:(
So here's a quick fix:
val myCar: Car = new Car extends DieselEngine {}

… but that's cheating (a bit)!
And it doesn't wor...
What we need is a way to wire up and assemble
our components without changing their identity
Enter: The Cake Pattern
Ok, so here's the recipe:
For each component in our system, supply a
Component trait, that declares:
● Any dependent components, using self-types
● ...
trait EngineComponent {
trait Engine {
private var running = false
def start(): Unit = { /* as before */ }
def stop(): Uni...
trait CarComponent {
this: EngineComponent => // gives access to engine
trait Car {
def drive(): Unit
def park(): Unit
}
p...
Great, now let's tie it all together:
(remember a Car has a couple more components beside an Engine)
object App extends CarComponent with EngineComponent with
FuelTankComponent with GearboxComponent {
override protected val...
If we want to write a CarTest, we can
provide mock instances for the
components we're not testing:
val testCar = new CarComponent with EngineComponent with FuelTankComponent
with GearboxComponent {
override protected val ...
Time to recap
The Cake Pattern
+ Environment specific assembly of components
+ Compile-time checked, typesafe
+ Everything is immutable
...
Remember this:
trait Car {
engine: Engine =>
def drive() { /* start engine */ }
def park() { /* stop engine */ }
}
object ...
> javap com.example.MyCar
Compiled from "Car.scala"
public final class com.example.MyCar extends java.lang.Object{
public ...
Imagine Car and Engine are part of a library that
I'm using to construct MyCar.
Any change to library-private interactions...
http://4.bp.blogspot.com/-OHTIQo-k2_k/Tmjkj66eIYI/AAAAAAAACfY/n1Vj1fseVQ0/s1600/Boom.jpg
Ergo:
Be very careful exposing self-types
as part of a library
Fin.
Sources:
●
●
●
●
●
●
●
●
●

M. Odersky, L. Spoon, B. Venners: Programming in Scala, 2nd Edition
C. S. Horstmann: Scala for...
Scala Self Types by Gregor Heine, Principal Software Engineer at Gilt
Upcoming SlideShare
Loading in …5
×

Scala Self Types by Gregor Heine, Principal Software Engineer at Gilt

6,559 views

Published on

Published in: Technology
2 Comments
15 Likes
Statistics
Notes
No Downloads
Views
Total views
6,559
On SlideShare
0
From Embeds
0
Number of Embeds
2,427
Actions
Shares
0
Downloads
57
Comments
2
Likes
15
Embeds 0
No embeds

No notes for slide

Scala Self Types by Gregor Heine, Principal Software Engineer at Gilt

  1. 1. http://image.motortrend.com/f/features/consumer/1301_chevrolet_corvette_60_years_american_icon_part_1/42023018/1961-Chevrolet-Corvette-front.jpg SCALA SELF-TYPES Gregor Heine, Gilt Groupe
  2. 2. Lets build a car...
  3. 3. Components ● ● ● ● ● ● ● ● Engine Fuel Tank Wheels Gearbox Steering Accelerator Clutch etc...
  4. 4. OK, let's write some interfaces
  5. 5. trait Engine { def start(): Unit def stop(): Unit def isRunning(): Boolean def fuelType: FuelType } trait Car { def drive(): Unit def park(): Unit }
  6. 6. Well actually, in Scala we can also add some implementation
  7. 7. trait Engine { private var running = false def start(): Unit = { if (!running) println("Engine started") running = true } def stop(): Unit = { if (running) println("Engine stopped") running = false } def isRunning(): Boolean = running def fuelType: FuelType } trait DieselEngine extends Engine { override val fuelType = FuelType.Diesel }
  8. 8. Fine, so how do we put an Engine into a Car?
  9. 9. First attempt: Inheritance-based assembly
  10. 10. trait Car extends Engine { def drive() { start() println("Vroom vroom") } def park() { if (isRunning() ) println("Break!") stop() } } val myCar = new Car extends DieselEngine
  11. 11. Hmm, this isn't great: A Car is also an Engine :( This should really be a has-a relation, right?
  12. 12. Second attempt: Composition-based assembly
  13. 13. trait Car { def engine : Engine def drive() { engine.start() println("Vroom vroom") } def park() { if (engine.isRunning() ) println("Break!") engine.stop() } } val myCar = new Car { override val engine = new DieselEngine() }
  14. 14. Hmm OK, a Car has an Engine. That's better. But...
  15. 15. There is no guarantee that the Engine in myCar isn't used in another Car! :(
  16. 16. If only there was a way to "mix-in" an Engine into my car rather than supplying it from the outside Enter: self-types
  17. 17. “A self type of a trait is the assumed type of this, the receiver, to be used within the trait. Any concrete class that mixes in the trait must ensure that its type conforms to the trait’s self type.” Programming in Scala
  18. 18. Erm, what, what, what? Fine, let's look at an example:
  19. 19. trait Car { this: Engine => // self-type def drive() { start() println("Vroom vroom") } def park() { println("Break!") stop() } } object MyCar extends Car with DieselEngine
  20. 20. trait Car { this: Engine => // self-type def drive() { Looks a bit like composition-based assembly start() println("Vroom vroom") } def park() { println("Break!") stop() } Looks like inheritance-based assembly } object MyCar extends Car with DieselEngine
  21. 21. So what's happening here?
  22. 22. ● Self-types are used with traits ● Explicitly declare the type of the value this ● Specify the requirements on any concrete class or instance the trait is mixed into. ● Declare a dependency of the trait on another type: “In order to use me you have to be one of those”
  23. 23. So, tell me more about self-types
  24. 24. No need to call the self-type "this" You can use this-aliasing to give it a different name:
  25. 25. trait Car { engine: Engine => def drive() { engine.start() println("Vroom vroom") } } Useful for nested classes or traits, where accessing a particular this would otherwise be difficult
  26. 26. Self-types don’t automatically inherit: trait HondaCar extends Car // error: self-type HondaCar does not conform to Car's selftype Car with Engine Need to repeat the self-type in subtypes: trait HondaCar extends Car { this: Engine => // ... }
  27. 27. A self-type can require multiple types: trait Car { this: Engine with FuelTank with GearBox => // ... } Used when the trait has multiple dependencies
  28. 28. The self-type can be a structural type: trait Car { this: { def start: Unit def stop: Unit } => // ... } Allows for safe mixins with duck-typing. (Useful when interacting with external dependencies)
  29. 29. So all is good with self-types?
  30. 30. Hmm, well no: val myCar = new Car extends DieselEngine {} myCar is still a Car and an Engine :(
  31. 31. So here's a quick fix: val myCar: Car = new Car extends DieselEngine {} … but that's cheating (a bit)! And it doesn't work for singletons: object MyCar extends Car with DieselEngine
  32. 32. What we need is a way to wire up and assemble our components without changing their identity
  33. 33. Enter: The Cake Pattern
  34. 34. Ok, so here's the recipe:
  35. 35. For each component in our system, supply a Component trait, that declares: ● Any dependent components, using self-types ● A trait describing the component's interface ● An abstract val that will be instantiated with an instance of the component ● Optionally, implementations of the component interface
  36. 36. trait EngineComponent { trait Engine { private var running = false def start(): Unit = { /* as before */ } def stop(): Unit = {/* as before */ } def isRunning: Boolean = running def fuelType: FuelType } protected val engine : Engine protected class DieselEngine extends Engine { override val fuelType = FuelType.Diesel } }
  37. 37. trait CarComponent { this: EngineComponent => // gives access to engine trait Car { def drive(): Unit def park(): Unit } protected val car: Car protected class HondaCar extends Car { override def drive() { engine.start() println("Vroom vroom") } override def park() { … } } }
  38. 38. Great, now let's tie it all together: (remember a Car has a couple more components beside an Engine)
  39. 39. object App extends CarComponent with EngineComponent with FuelTankComponent with GearboxComponent { override protected val engine = new DieselEngine() override protected val fuelTank = new FuelTank(capacity = 60) override protected val gearBox = new FiveGearBox() override val car = new HondaCar() } MyApp.car.drive() MyApp.car.park()
  40. 40. If we want to write a CarTest, we can provide mock instances for the components we're not testing:
  41. 41. val testCar = new CarComponent with EngineComponent with FuelTankComponent with GearboxComponent { override protected val engine = mock[Engine] // mock engine override protected val fuelTank = mock[FuelTank] // mock tank override protected val gearBox = new FiveGearBox() // an actual gearbox override val car = new HondaCar() }.car
  42. 42. Time to recap
  43. 43. The Cake Pattern + Environment specific assembly of components + Compile-time checked, typesafe + Everything is immutable + No external DI descriptor - Can become hard to read and understand - May be difficult to configure components - No control over initialization order - Self-types prone to fragile base class problem
  44. 44. Remember this: trait Car { engine: Engine => def drive() { /* start engine */ } def park() { /* stop engine */ } } object MyCar extends Car with DieselEngine How did they do it? Let's decompile MyCar.class
  45. 45. > javap com.example.MyCar Compiled from "Car.scala" public final class com.example.MyCar extends java.lang.Object{ public static void park(); public static void drive(); public static boolean isRunning(); public static void stop(); public static void start(); /* … */ } All functions from Car and Engine have been "lifted" into the top-level class!
  46. 46. Imagine Car and Engine are part of a library that I'm using to construct MyCar. Any change to library-private interactions between these traits are not reflected in MyCar. class
  47. 47. http://4.bp.blogspot.com/-OHTIQo-k2_k/Tmjkj66eIYI/AAAAAAAACfY/n1Vj1fseVQ0/s1600/Boom.jpg
  48. 48. Ergo: Be very careful exposing self-types as part of a library
  49. 49. Fin.
  50. 50. Sources: ● ● ● ● ● ● ● ● ● M. Odersky, L. Spoon, B. Venners: Programming in Scala, 2nd Edition C. S. Horstmann: Scala for the Impatient M. Odersky: Scalable Component Abstractions http://lampwww.epfl.ch/~odersky/papers/ScalableComponent.pdf J. Bonér: Real-World Scala: Dependency Injection (DI) http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di A Tour of Scala: Explicitly Typed Self References http://www.scala-lang.org/old/node/124 Cake pattern in depth http://www.cakesolutions.net/teamblogs/2011/12/19/cake-pattern-in-depth Dependency Injection In Scala using Self Type Annotations http://blog.knoldus.com/2013/01/21/dependency-injection-in-scala-using-self-type-annotations Cake Pattern in Scala / Self type annotations / Explicitly Typed Self References - explained https://coderwall.com/p/t_rapw DI in Scala: Cake Pattern pros & cons http://www.warski.org/blog/2011/04/di-in-scala-cake-pattern-pros-cons

×