Modules and objects
Modules are a way to organize programs. They are interchangeable and pluggable pieces of code that have well-defined interfaces and hidden implementations. In Java, modules are organized in packages. In Scala, modules are objects; just like everything else. This means that they can be parameterized, extended, and passed as parameters, and so on.
Scala modules can provide requirements in order to be used.
Using modules
We already established that modules and objects are also unified in Scala. This means that we can pass an entire module around our application. It would be useful, however, to show what a module actually looks like. Here is an example:
trait Tick { trait Ticker { def count(): Int def tick(): Unit } def ticker: Ticker }
Here, Tick
is just an interface to one of our modules. The following is its implementation:
trait TickUser extends Tick { class TickUserImpl extends Ticker { var curr = 0 override def count(): Int = curr override def tick(): Unit = { curr = curr + 1 } } object ticker extends TickUserImpl }
The TickUser
trait is an actual module. It implements Tick
and contains the code hidden inside it. We create a singleton object that will carry the implementation. Note how the name in the object is the same as the method in Tick
. This would cover the need to implement it when mixing in the trait.
Similarly, we can define another interface and an implementation as follows:
trait Alarm { trait Alarmer { def trigger(): Unit } def alarm: Alarmer }
The implementation will be this:
trait AlarmUser extends Alarm with Tick { class AlarmUserImpl extends Alarmer { override def trigger(): Unit = { if (ticker.count() % 10 == 0) { System.out.println(s"Alarm triggered at ${ticker.count()}!") } } } object alarm extends AlarmUserImpl }
What is interesting here is that we extended both modules in the AlarmUser
one. This shows how modules could be made to be dependent on each other. Finally, we can use our modules as follows:
object ModuleDemo extends AlarmUser with TickUser { def main(args: Array[String]): Unit = { System.out.println("Running the ticker. Should trigger the alarm every 10 times.") (1 to 100).foreach { case i => ticker.tick() alarm.trigger() } } }
In order for ModuleDemo
to use the AlarmUser
module, it is also required by the compiler to mix in TickUser
or any module that mixes in Tick
. This provides a possibility to plug in a different functionality.
The output of the program will be this:
Running the ticker. Should trigger the alarm every 10 times. Alarm triggered at 10! Alarm triggered at 20! Alarm triggered at 30! Alarm triggered at 40! Alarm triggered at 50! Alarm triggered at 60! Alarm triggered at 70! Alarm triggered at 80! Alarm triggered at 90! Alarm triggered at 100!
Note
Modules in Scala can be passed as any other object. They are extendable, interchangeable, and their implementation is hidden.