Following from this question, I have now the following:
case class Pet(val name: String)
trait ConfigComponent {
type Config
def config: Config
}
trait VetModule extends ConfigComponent {
type Config <: VetModuleConfig
def vet: Vet
trait Vet {
def vaccinate(pet: Pet)
}
trait VetModuleConfig {
def extra: String
}
}
trait VetModuleImpl extends VetModule {
override def vet: Vet = VetImpl
object VetImpl extends Vet {
def vaccinate(pet: Pet) = println("Vaccinate:" + pet + " " + config.extra)
}
}
trait AnotherModule extends ConfigComponent {
type Config <: AnotherConfig
def getLastName(): String
trait AnotherConfig {
val lastName: String
}
}
trait AnotherModuleImpl extends AnotherModule {
override def getLastName(): String = config.lastName
}
trait PetStoreModule extends ConfigComponent {
type Config <: PetStoreConfig
def petStore: PetStore
trait PetStore {
def sell(pet: Pet): Unit
}
trait PetStoreConfig {
val petStoreName: String
}
}
trait PetStoreModuleImpl extends PetStoreModule {
self: VetModule with AnotherModule =>
override def petStore: PetStore = PetstoreImpl
object PetstoreImpl extends PetStore {
def sell(pet: Pet) {
vet.vaccinate(pet)
println(s"Sold $pet! [Store: ${config.petStoreName}, lastName: $getLastName]")
}
}
}
class MyApp extends PetStoreModuleImpl with VetModuleImpl with AnotherModuleImpl {
type Config = PetStoreConfig with AnotherConfig
override object config extends PetStoreConfig with AnotherConfig {
val petStoreName = "MyPetStore"
val lastName = "MyLastName"
}
petStore.sell(new Pet("Fido"))
}
object Main {
def main(args: Array[String]) {
new MyApp
}
}
I get the following compile error:
value petStoreName is not a member of PetStoreModuleImpl.this.Config
println(s"Sold $pet! [Store: ${config.petStoreName}, lastName: $getLastName]")
^
This is actually the error I have been struggling with. Can somebody explain why it occurs? Currently, as a work-around, I just explicitly cast the config object in each module implementation.
What you've written should work, but doesn't, because of this bug.
There are a number of workarounds you could use. Adding
type Config = PetStoreConfig with AnotherConfig
to your module implementation is probably a little less unpleasant than casting.Update: As som-snytt notes in a comment and answer, adding
with PetStoreModuleImpl
(crucially notwith PetStoreModule
, as you might expect) to the end of the self-type is a better solution.As a footnote: as discussed in the comments on SI-7255, the Dependent Object Types calculus (which is designed to be "a new foundation for Scala's type system") will address this "fundamental problem in Scala's type system".