I'm trying to design a small module system for my application such that I can do this:
new MyApplication extends Module1 with Module2 ... with ModuleN
In order to let my modules register themselves with the application, I also have:
trait ModuleRegistry {
def register(start: () => Unit) = // Stores start functions in a list
}
trait Module {
self: ModuleRegistry =>
self.register(start)
def start(): Unit
}
class Application extends ModuleRegistry {
}
trait Module1 extends Module {
...
}
The idea is that modules can register a function with the registry to be called when the application starts up. Unfortunately, the Scala compiler forces me to do this:
trait Module1 extends Module {
self: ModuleRegistry =>
}
meaning that all implementations of a module have to explicitly self-type themselves with the registry, when ideally they'd have no knowledge of it.
So my questions are:
- Why does the compiler force me to re-specify this self-type on an extending trait? Ideally I don't even want the extending trait to 'see' this type, as it should be handled by the base Module trait. The 'concrete' module is meant to be implemented by 3rd parties, so exposing the registry to them seems kinda ugly.
- Is there a better way to do what I'm trying to achieve in Scala so that modules can be freely mixed in?
Module1andModule2both definestart....