baking delicious modularity in scala
DESCRIPTION
This was a presentation I gave in October 2013 to the Waterloo Scala Meetup. It's probably the 437th presentation on Scala modularity to date. There's tons of them, but the group was curious about how it was done and wanted the personal touch, so I gave it to them...TRANSCRIPT
Baking
Delicious
Modularization
in ScalaDerek Wyatt
Twitter: @derekwyattEmail: [email protected]
Thursday, December 12, 13
Object Oriented Modularity
OOD gives us...
Thursday, December 12, 13
Object Oriented Modularity
OOD gives us... ClassesAbstract ClassesInterfacespublic
privateprotected
Inheritance&Composition
Thursday, December 12, 13
Object Oriented Modularity
OOD gives us... ClassesAbstract ClassesInterfacespublic
privateprotected
C++ gave us... Multiple InheritanceJust Plain Evil Right?. . .
Inheritance&Composition
Thursday, December 12, 13
The Diamond ProblemA
B CisAis
A
Thursday, December 12, 13
The Diamond Problem
D
A
B CisAis
A
isAisA
Thursday, December 12, 13
The Diamond Problem
D
?
A
B CisAis
A
isAisA
Who initializes A?
Thursday, December 12, 13
The Diamond Problem
D
?
A
B CisAis
A
isAisA
Who initializes A?
B needs it initializedSo does C They can’t both do it!
Thursday, December 12, 13
The Diamond Problem
D
?
A
B CisAis
A
isAisA
Who initializes A?
B needs it initializedSo does C They can’t both do it!
Enter: Virtual Inheritance
Thursday, December 12, 13
The Diamond Problem
D
?
A
B CisAis
A
isAisA
Who initializes A?
B needs it initializedSo does C They can’t both do it!
Enter: Virtual InheritanceConvincing the world for decades that
Multiple Inheritance is BADThursday, December 12, 13
Java’s Solution to Multiple Inheritance
Multiple Inheritance
Thursday, December 12, 13
Java’s Solution to Multiple Inheritance
Multiple Inheritance
The powers that be just went and tossed it out
Thursday, December 12, 13
Java’s Solution to Multiple Inheritance
Multiple Inheritance
The powers that be just went and tossed it out
Implementing multiple interfaces and extending one abstract classis not equivalent
Thursday, December 12, 13
Java’s Solution to Multiple Inheritance
Multiple Inheritance
The powers that be just went and tossed it out
Implementing multiple interfaces and extending one abstract classis not equivalentImplementing the multiple interfaces is work
Thursday, December 12, 13
Java’s Solution to Multiple Inheritance
Multiple Inheritance
The powers that be just went and tossed it out
Implementing multiple interfaces and extending one abstract classis not equivalentImplementing the multiple interfaces is workSharing code amongst implementations is hard Compose and
delegate
Thursday, December 12, 13
Java’s Solution to Multiple Inheritance
Multiple Inheritance
The powers that be just went and tossed it out
Implementing multiple interfaces and extending one abstract classis not equivalentImplementing the multiple interfaces is workSharing code amongst implementations is hardInconsistencies show up very quickly and maintenance is a pain
Compose and
delegate
Thursday, December 12, 13
Java’s Solution to Multiple Inheritance
Multiple Inheritance
The powers that be just went and tossed it out
Implementing multiple interfaces and extending one abstract classis not equivalentImplementing the multiple interfaces is workSharing code amongst implementations is hardInconsistencies show up very quickly and maintenance is a pain
Compose and
delegate#FAIL
Thursday, December 12, 13
Scala’s Solution to Multiple Inheritance
Thursday, December 12, 13
Scala’s Solution to Multiple Inheritance
Crush the Diamond!
Thursday, December 12, 13
Scala’s Solution to Multiple Inheritance
Crush the Diamond!Scala integrates inheritance with mixin functionality
Thursday, December 12, 13
Scala’s Solution to Multiple Inheritance
Crush the Diamond!Scala integrates inheritance with mixin functionalityTraits provide the construct for mixins
trait Atrait B extends Atrait C extends Aclass D extends B with C
Thursday, December 12, 13
Scala’s Solution to Multiple Inheritance
Crush the Diamond!Scala integrates inheritance with mixin functionalityTraits provide the construct for mixins
trait Atrait B extends Atrait C extends Aclass D extends B with C
Translates to
Any
AnyRef
A
B
C
D
Initia
lize
Thursday, December 12, 13
Scala’s Solution to Multiple Inheritance
Crush the Diamond!Scala integrates inheritance with mixin functionalityTraits provide the construct for mixins
trait Atrait B extends Atrait C extends Aclass D extends B with C
If a conflict exists between B and C with respect to A, then C wins.
Translates to
Any
AnyRef
A
B
C
D
Initia
lize
Thursday, December 12, 13
Origins of the Cake
Randomizer
Persistence
PicRetriever DatabasePersistence
HashMap TestPersistence
RESTPersistence
RandomRandomizer
Specifier TestRandomizer
def randomPic: Future[Pic]
Dependency Injection and Modularity
Thursday, December 12, 13
Origins of the Cake
Randomizer
Persistence
PicRetriever DatabasePersistence
HashMap TestPersistence
RESTPersistence
RandomRandomizer
Specifier TestRandomizer
def randomPic: Future[Pic]
Dependency Injection and Modularity
Origins of the Cake
Thursday, December 12, 13
Origins of the Cake
Randomizer
Persistence
PicRetriever DatabasePersistence
HashMap TestPersistence
RESTPersistence
RandomRandomizer
Specifier TestRandomizer
def randomPic: Future[Pic]
Declare reliance on
these modules
Dependency Injection and Modularity
Origins of the Cake
Thursday, December 12, 13
Origins of the Cake
Randomizer
Persistence
PicRetriever DatabasePersistence
HashMap TestPersistence
RESTPersistence
RandomRandomizer
Specifier TestRandomizer
def randomPic: Future[Pic]
Declare reliance on
these modules
new PicRetriever extends Database with RandomChoose concrete implementations
Dependency Injection and Modularity
Origins of the Cake
Thursday, December 12, 13
Origins of the Cake
Randomizer
Persistence
PicRetriever
DatabasePersistence
HashMap TestPersistence
RESTPersistence
RandomRandomizer
Specifier TestRandomizer
def randomPic: Future[Pic]
Declare reliance on
these modules
new PicRetriever extends Database with RandomChoose concrete implementations
Dependency Injection and Modularity
PicRetriever
mixes in the
interface
and the
implementation
Origins of the Cake
Thursday, December 12, 13
The Basic Cupcaketrait Persistence { def pic(idx: Int): Future[Pic] = ??? def totalPics: Future[Int] = ???}
trait Randomizer { def nextInt(upperBound: Int): Int = ???}
Thursday, December 12, 13
The Basic Cupcaketrait Persistence { def pic(idx: Int): Future[Pic] = ??? def totalPics: Future[Int] = ???}
class PicRetriever { this: Persistence with Randomizer => def randomPic: Future[Pic] = ???}
object PicRetriever { def production: PicRetriever = new PicRetriever with Persistence with Randomizer}
trait Randomizer { def nextInt(upperBound: Int): Int = ???}
Declare PicRetri
ever
self type
Thursday, December 12, 13
The Basic Cupcaketrait Persistence { def pic(idx: Int): Future[Pic] = ??? def totalPics: Future[Int] = ???}
class PicRetriever { this: Persistence with Randomizer => def randomPic: Future[Pic] = ???}
object PicRetriever { def production: PicRetriever = new PicRetriever with Persistence with Randomizer}
trait Randomizer { def nextInt(upperBound: Int): Int = ???}
Declare PicRetri
ever
self type
Define factory method that makes construction simpler
Thursday, December 12, 13
The Basic Cupcaketrait Persistence { def pic(idx: Int): Future[Pic] = ??? def totalPics: Future[Int] = ???}
class PicRetriever { this: Persistence with Randomizer => def randomPic: Future[Pic] = ???}
object PicRetriever { def production: PicRetriever = new PicRetriever with Persistence with Randomizer}
trait Randomizer { def nextInt(upperBound: Int): Int = ???}
You could derive new Persistence and Randomizer traits and instantiate accordingly. Not great, but it does get the job done.
Declare PicRetri
ever
self type
Define factory method that makes construction simpler
Thursday, December 12, 13
Adding a LayerThe lack of an “interface” requires overrides in the derivations
Thursday, December 12, 13
Adding a LayerThe lack of an “interface” requires overrides in the derivations
We can add that layer, making derivations easier to write
Thursday, December 12, 13
Adding a LayerThe lack of an “interface” requires overrides in the derivations
We can add that layer, making derivations easier to write
trait Persistence { def pic(idx: Int): Future[Pic] def totalPics: Future[Int]}trait Database extends Persistence { val dbUrl: String def pic(idx: Int): Future[Pic] = ??? def totalPics: Int = ???}
trait Randomizer { def nextInt(upperBound: Int): Int}trait RealRandomizer extends Randomizer { def nextInt(upperBound: Int): Int = ???}
Thursday, December 12, 13
Adding a LayerThe lack of an “interface” requires overrides in the derivations
We can add that layer, making derivations easier to write
trait Persistence { def pic(idx: Int): Future[Pic] def totalPics: Future[Int]}trait Database extends Persistence { val dbUrl: String def pic(idx: Int): Future[Pic] = ??? def totalPics: Int = ???}
object PicRetriever { def production: PicRetriever = new PicRetriever with Database with RealRandomizer}
trait Randomizer { def nextInt(upperBound: Int): Int}trait RealRandomizer extends Randomizer { def nextInt(upperBound: Int): Int = ???}
Thursday, December 12, 13
Eliminating Naming Conflicts
Thursday, December 12, 13
Eliminating Naming ConflictsThere’s nothing to stop you from tromping all over your names
Thursday, December 12, 13
Eliminating Naming ConflictsThere’s nothing to stop you from tromping all over your namesIt’s a good idea to give namespaces to the mixed-in modules
trait PersistenceComponent { val persistence: Persistence trait Persistence { def pic(idx: Int): Future[Pic] def totalPics: Future[Int] }}
trait RandomizerComponent { val randomizer: Randomizer trait Randomizer { def nextInt(upperBound: Int): Int }}
Thursday, December 12, 13
Eliminating Naming ConflictsThere’s nothing to stop you from tromping all over your namesIt’s a good idea to give namespaces to the mixed-in modules
trait PersistenceComponent { val persistence: Persistence trait Persistence { def pic(idx: Int): Future[Pic] def totalPics: Future[Int] }}
trait RandomizerComponent { val randomizer: Randomizer trait Randomizer { def nextInt(upperBound: Int): Int }}
Everything can be
solved with another
layer of indirection
Thursday, December 12, 13
Eliminating Naming ConflictsThere’s nothing to stop you from tromping all over your namesIt’s a good idea to give namespaces to the mixed-in modules
trait PersistenceComponent { val persistence: Persistence trait Persistence { def pic(idx: Int): Future[Pic] def totalPics: Future[Int] }}
trait RandomizerComponent { val randomizer: Randomizer trait Randomizer { def nextInt(upperBound: Int): Int }}
class PicRetriever { this: PersistenceComponent with RandomizerComponent => // ...}object PicRetriever { def production: PicRetriever = new PicRetriever with ProdPersistence with ProdRandomizer { val persistence = new Persistence { } val randomizer = new Randomizer { }}
Thursday, December 12, 13
Taming the Test
Thursday, December 12, 13
trait TestRand
omizerComp ext
ends Randomize
rComponent {
val randomiz
er: Randomizer
trait TestRa
ndomizer exten
ds Randomizer
{
val i: Int
def nextIn
t(upperBound:
Int): Int = i
}}
Taming the Test
Thursday, December 12, 13
trait TestRand
omizerComp ext
ends Randomize
rComponent {
val randomiz
er: Randomizer
trait TestRa
ndomizer exten
ds Randomizer
{
val i: Int
def nextIn
t(upperBound:
Int): Int = i
}}
trait TestPersiste
nceComp extends Pe
rsistenceComponent
{
val persistence:
PersistenceCompon
ent
trait TestPersis
tence extends Pers
istence {
val pics: Vect
or[Pic]
def pic(idx: I
nt): Future[Pic] =
Future(pics(idx))
def totalPics:
Future[Int] = Fut
ure(pics.size)
}}
Taming the Test
Thursday, December 12, 13
trait TestRand
omizerComp ext
ends Randomize
rComponent {
val randomiz
er: Randomizer
trait TestRa
ndomizer exten
ds Randomizer
{
val i: Int
def nextIn
t(upperBound:
Int): Int = i
}}
trait TestPersiste
nceComp extends Pe
rsistenceComponent
{
val persistence:
PersistenceCompon
ent
trait TestPersis
tence extends Pers
istence {
val pics: Vect
or[Pic]
def pic(idx: I
nt): Future[Pic] =
Future(pics(idx))
def totalPics:
Future[Int] = Fut
ure(pics.size)
}}
Taming the Test
object TestPicRetriever { def test(num: Int, images: Vector[Pic]): PicRetriever = new PicRetriever with TestPersistenceComp with TestRandomizerComp { val persistence = new TestPersistence { val pics = images } val randomizer = new TestRandomizer { val i = num } }
Thursday, December 12, 13
trait TestRand
omizerComp ext
ends Randomize
rComponent {
val randomiz
er: Randomizer
trait TestRa
ndomizer exten
ds Randomizer
{
val i: Int
def nextIn
t(upperBound:
Int): Int = i
}}
trait TestPersiste
nceComp extends Pe
rsistenceComponent
{
val persistence:
PersistenceCompon
ent
trait TestPersis
tence extends Pers
istence {
val pics: Vect
or[Pic]
def pic(idx: I
nt): Future[Pic] =
Future(pics(idx))
def totalPics:
Future[Int] = Fut
ure(pics.size)
}}
Taming the Test
object TestPicRetriever { def test(num: Int, images: Vector[Pic]): PicRetriever = new PicRetriever with TestPersistenceComp with TestRandomizerComp { val persistence = new TestPersistence { val pics = images } val randomizer = new TestRandomizer { val i = num } }
val pic = loadPic(“pic1”)val retriever = TestPicRetriever.test(0, Vector(pic))Await.result(retriever.randomPic, 1.second) should be (pic)
Thursday, December 12, 13
Enough Analogues... Modularity!
Thursday, December 12, 13
Enough Analogues... Modularity!
You don’t have to “self type” in order to establish the contract
Thursday, December 12, 13
Enough Analogues... Modularity!
You don’t have to “self type” in order to establish the contract
Abstract types and constraints can solve the problem with greater flexibility
Thursday, December 12, 13
Enough Analogues... Modularity!
You don’t have to “self type” in order to establish the contract
Abstract types and constraints can solve the problem with greater flexibility
trait PersistenceLike { def pic(idx: Int): Future[Pic] def totalPics: Future[Int]}
trait RandomizerLike { def nextInt(upperBound: Int): Int}
Thursday, December 12, 13
Enough Analogues... Modularity!
You don’t have to “self type” in order to establish the contract
Abstract types and constraints can solve the problem with greater flexibility
trait PersistenceLike { def pic(idx: Int): Future[Pic] def totalPics: Future[Int]}
trait PicRetriever { type Persistence <: PersistenceLike type Randomizer <: RandomizerLike
val persistence: Persistence val randomizer: Randomizer
// ...}
trait RandomizerLike { def nextInt(upperBound: Int): Int}
Thursday, December 12, 13
Enough Analogues... Modularity!
You don’t have to “self type” in order to establish the contract
Abstract types and constraints can solve the problem with greater flexibility
trait PersistenceLike { def pic(idx: Int): Future[Pic] def totalPics: Future[Int]}
trait PicRetriever { type Persistence <: PersistenceLike type Randomizer <: RandomizerLike
val persistence: Persistence val randomizer: Randomizer
// ...}
trait RandomizerLike { def nextInt(upperBound: Int): Int}
Abstract types define enough to work with for the PicRetriever, but allow for further specification later
Thursday, December 12, 13
Use Case... Configuration
Thursday, December 12, 13
Use Case... Configuration
Let’s say you want to declare a method for configuring objects
trait Configclass DBConfig extends Config { val dbHost: String val dbPort: Int}class RandomizerConfig extends Config { val randomSeed: Int val randomScale: Int}class RESTConfig extends Config { val restHost: String val restPort: Int}
Thursday, December 12, 13
Use Case... Configuration
Let’s say you want to declare a method for configuring objects
You want generality, so you can’t pass multiple configs
abstract class Component(config: Config) trait Configclass DBConfig extends Config { val dbHost: String val dbPort: Int}class RandomizerConfig extends Config { val randomSeed: Int val randomScale: Int}class RESTConfig extends Config { val restHost: String val restPort: Int}
Thursday, December 12, 13
Use Case... Configuration
Let’s say you want to declare a method for configuring objects
You want generality, so you can’t pass multiple configs
abstract class Component(config: Config)
class Database(config: Config) extends Component(config) { require(config.isInstanceOf[DBConfig])}
class Randomizer(config: Config) extends Component(config) { require(config.isInstanceOf[RandomizerConfig])}
class REST(config: Config) extends Component(config) { require(config.isInstanceOf[RESTConfig])}
trait Configclass DBConfig extends Config { val dbHost: String val dbPort: Int}class RandomizerConfig extends Config { val randomSeed: Int val randomScale: Int}class RESTConfig extends Config { val restHost: String val restPort: Int}
Thursday, December 12, 13
Use Case... Configuration
Let’s say you want to declare a method for configuring objects
You want generality, so you can’t pass multiple configs
abstract class Component(config: Config)
class Database(config: Config) extends Component(config) { require(config.isInstanceOf[DBConfig])}
class Randomizer(config: Config) extends Component(config) { require(config.isInstanceOf[RandomizerConfig])}
class REST(config: Config) extends Component(config) { require(config.isInstanceOf[RESTConfig])}
trait Configclass DBConfig extends Config { val dbHost: String val dbPort: Int}class RandomizerConfig extends Config { val randomSeed: Int val randomScale: Int}class RESTConfig extends Config { val restHost: String val restPort: Int}
Ugly, Unsafe, and
Annoying
Thursday, December 12, 13
Configuring through Inverse Generality
Database Config
REST Config
Randomization Config
Thursday, December 12, 13
Configuring through Inverse GeneralityConfig
IsAIsA
IsA
Database Config
REST Config
Randomization Config
Thursday, December 12, 13
Configuring through Inverse GeneralityConfig Empty generalization
IsAIsA
IsA
Database Config
REST Config
Randomization Config
Thursday, December 12, 13
Configuring through Inverse GeneralityDatabase Config
REST Config
Randomization Config
DatabaseREST
Randomization
CONFIG OBJECT
Highly Specificand Type safe
Thursday, December 12, 13
Thickly Typed Objects Kinda stolen from Daniel Spiewak❊
Thursday, December 12, 13
Thickly Typed Objects Kinda stolen from Daniel Spiewak❊
trait ConfigComponent { type Config def config: Config}
Declare a way to configure things
Thursday, December 12, 13
Thickly Typed Objects Kinda stolen from Daniel Spiewak❊
trait DBComponent extends PersistenceComponent with ConfigComponent { type Config <: DBConfig
val persistence = new DB class DB extends Persistence { def pic(idx: Int): Future[Pic] = ??? def totalPics: Future[Int] = ??? }
trait DBConfig { val dbHost: String val dbPort: Int }}
trait ConfigComponent { type Config def config: Config}
Declare a way to configure things
Thursday, December 12, 13
Thickly Typed Objects Kinda stolen from Daniel Spiewak❊
trait DBComponent extends PersistenceComponent with ConfigComponent { type Config <: DBConfig
val persistence = new DB class DB extends Persistence { def pic(idx: Int): Future[Pic] = ??? def totalPics: Future[Int] = ??? }
trait DBConfig { val dbHost: String val dbPort: Int }}
trait ConfigComponent { type Config def config: Config}
Declare a way to configure things
Define how to configure this component
Thursday, December 12, 13
Thickly Typed Objects Kinda stolen from Daniel Spiewak❊
trait DBComponent extends PersistenceComponent with ConfigComponent { type Config <: DBConfig
val persistence = new DB class DB extends Persistence { def pic(idx: Int): Future[Pic] = ??? def totalPics: Future[Int] = ??? }
trait DBConfig { val dbHost: String val dbPort: Int }}
trait ConfigComponent { type Config def config: Config}
Declare a way to configure things
Define how to configure this component
Repeat for REST and Randomizer
Thursday, December 12, 13
Thickly Typed Objects Continuedclass TheFinalThing extends DBComponent with RandomizerComponent with RESTComponent { type Config = DBConfig with RESTConfig with RandomzierConfig
object config extends DBConfig with RESTConfig with RandomzierConfig { val dbHost = “dbhost1” val dbPort = 56872 val restHost = “resthost1” val restPort = 8080 val randomSeed = System.currentTimeMillis.toInt val randomScale = 6 } // etc...}
Thursday, December 12, 13
Thickly Typed Objects Continuedclass TheFinalThing extends DBComponent with RandomizerComponent with RESTComponent { type Config = DBConfig with RESTConfig with RandomzierConfig
object config extends DBConfig with RESTConfig with RandomzierConfig { val dbHost = “dbhost1” val dbPort = 56872 val restHost = “resthost1” val restPort = 8080 val randomSeed = System.currentTimeMillis.toInt val randomScale = 6 } // etc...}
The config object now conforms to the types
requested by everything that’s been mixed in.
Thursday, December 12, 13
Thickly Typed Objects Continuedclass TheFinalThing extends DBComponent with RandomizerComponent with RESTComponent { type Config = DBConfig with RESTConfig with RandomzierConfig
object config extends DBConfig with RESTConfig with RandomzierConfig { val dbHost = “dbhost1” val dbPort = 56872 val restHost = “resthost1” val restPort = 8080 val randomSeed = System.currentTimeMillis.toInt val randomScale = 6 } // etc...}
The config object now conforms to the types
requested by everything that’s been mixed in.
Each Component can access the fields of their slice of the config object without the need for casting. The compiler already
knows it’s right.
Thursday, December 12, 13
Pain and Frustration
Thursday, December 12, 13
Pain and FrustrationCakes generally involve abstract vals
Thursday, December 12, 13
Pain and FrustrationCakes generally involve abstract valsThey also involve “Multiple Inheritance”
Thursday, December 12, 13
Pain and FrustrationCakes generally involve abstract valsThey also involve “Multiple Inheritance”
This presents an interesting initialization problem
Thursday, December 12, 13
Pain and FrustrationCakes generally involve abstract valsThey also involve “Multiple Inheritance”
This presents an interesting initialization problem
trait A { val s: String val t: String = s.substring(0, 2)}
trait B { val s: String = “What?”}
Thursday, December 12, 13
Pain and FrustrationCakes generally involve abstract valsThey also involve “Multiple Inheritance”
This presents an interesting initialization problem
trait A { val s: String val t: String = s.substring(0, 2)}
trait B { val s: String = “What?”}
class C extends A with B
val c = new C
Thursday, December 12, 13
Pain and FrustrationCakes generally involve abstract valsThey also involve “Multiple Inheritance”
This presents an interesting initialization problem
trait A { val s: String val t: String = s.substring(0, 2)}
trait B { val s: String = “What?”}
class C extends A with B
val c = new C
Thursday, December 12, 13
Pain and FrustrationCakes generally involve abstract valsThey also involve “Multiple Inheritance”
This presents an interesting initialization problem
trait A { val s: String val t: String = s.substring(0, 2)}
trait B { val s: String = “What?”}
class C extends A with B
val c = new C
NPE!!!!!!
Thursday, December 12, 13
Null Pointer Exceptions?!
Thursday, December 12, 13
Null Pointer Exceptions?!We’re not used to these any more
Thursday, December 12, 13
Null Pointer Exceptions?!We’re not used to these any moreThey’re an artifact of initialization order and usage
Thursday, December 12, 13
Null Pointer Exceptions?!We’re not used to these any moreThey’re an artifact of initialization order and usage
ABC
Thursday, December 12, 13
Null Pointer Exceptions?!We’re not used to these any moreThey’re an artifact of initialization order and usage
ABC
Initialization Order
Thursday, December 12, 13
Null Pointer Exceptions?!We’re not used to these any moreThey’re an artifact of initialization order and usage
ABC
Initialization Order
A is referencing ‘s’
Thursday, December 12, 13
Null Pointer Exceptions?!We’re not used to these any moreThey’re an artifact of initialization order and usage
ABC
Initialization Order
A is referencing ‘s’
‘s’ hasn’t been initialized yet!
Thursday, December 12, 13
Null Pointer Exceptions?!We’re not used to these any moreThey’re an artifact of initialization order and usage
ABC
Initialization Order
A is referencing ‘s’
‘s’ hasn’t been initialized yet!The only sane value for ‘s’ at this point is null
Thursday, December 12, 13
Dealing with the Pain
Thursday, December 12, 13
Dealing with the PainMethods aren’t susceptible to this problem
Thursday, December 12, 13
Dealing with the PainMethods aren’t susceptible to this problem
Neither are lazy vals
Thursday, December 12, 13
Dealing with the PainMethods aren’t susceptible to this problem
Neither are lazy vals
Neither are constructor parameters
Thursday, December 12, 13
Dealing with the PainMethods aren’t susceptible to this problem
Neither are lazy vals
Neither are constructor parameters
trait A { val s: String val t: String = s.substring(0, 2)}
class B(val s) extends A
val b = new B(“What?”)
Thursday, December 12, 13
Dealing with the PainMethods aren’t susceptible to this problem
Neither are lazy vals
Neither are constructor parameters
trait A { val s: String val t: String = s.substring(0, 2)}
class B(val s) extends A
val b = new B(“What?”)
OK
Thursday, December 12, 13
Dealing with the PainMethods aren’t susceptible to this problem
Neither are lazy vals
Neither are constructor parameters
trait A { val s: String val t: String = s.substring(0, 2)}
class B(val s) extends A
val b = new B(“What?”)
trait A { val s: String val t: String = s.substring(0, 2)}
class B extends A { lazy val s = “What?”}
val b = new B
OK
Thursday, December 12, 13
Dealing with the PainMethods aren’t susceptible to this problem
Neither are lazy vals
Neither are constructor parameters
trait A { val s: String val t: String = s.substring(0, 2)}
class B(val s) extends A
val b = new B(“What?”)
trait A { val s: String val t: String = s.substring(0, 2)}
class B extends A { lazy val s = “What?”}
val b = new B
OK OK
Thursday, December 12, 13
Dealing with the PainMethods aren’t susceptible to this problem
Neither are lazy vals
Neither are constructor parameters
trait A { val s: String val t: String = s.substring(0, 2)}
class B(val s) extends A
val b = new B(“What?”)
trait A { val s: String val t: String = s.substring(0, 2)}
class B extends A { lazy val s = “What?”}
val b = new B
trait A { val s: String val t: String = s.substring(0, 2)}
class B extends A { def s = “What?”}
val b = new B
OK OK
Thursday, December 12, 13
Dealing with the PainMethods aren’t susceptible to this problem
Neither are lazy vals
Neither are constructor parameters
trait A { val s: String val t: String = s.substring(0, 2)}
class B(val s) extends A
val b = new B(“What?”)
trait A { val s: String val t: String = s.substring(0, 2)}
class B extends A { lazy val s = “What?”}
val b = new B
trait A { val s: String val t: String = s.substring(0, 2)}
class B extends A { def s = “What?”}
val b = new B
OKOK
OK
Thursday, December 12, 13
Now go get Fat
Derek WyattTwitter: @derekwyatt
Email: [email protected]
Eat lots of cakeAND
Maudularize Yore Coad
Thursday, December 12, 13