Singletons

Scala programmers can define objects without first defining a class and then instantiating it. Such objects are called singletons.

For example, a queuing simulation might require a single global clock to keep track of the number of elapsed cycles:

object clock:
   var cycle = 0
   def tick() = cycle += 1
   def time = cycle

With curly braces:

object clock {
   var cycle = 0
   def tick() = cycle += 1
   def time = cycle
}

Singletons are static

Since we can't instantiate classes before a program starts, how do we start the program in the first place? In Java this problem is solved by declaring main as a static method of some class. But there are no static methods or fields in Scala. However, singletons are static. Therefore, Scala programmers put main in a singleton:

object test:
   def main(args: Array[String]): Unit =
      for(i <- 0 to 50) clock.tick()
      println
("time = " + clock.cycle) // prints "time = 51"

Notes:

·       When we start a program all singletons are created before main is executed.

·       Singletons have global scope or visibility. They are visible to the entire program. For example, test doesn't need to create a pointer to clock, it can reference it directly.

·       In C terminology a value (variable, function, object, etc.) is static if it has global scope (visible everywhere) and global lifetime (exists the entire time the program runs).

·       Typically, a programs memory is partitioned into three segments: stack, heap, and static. Local variables reside in the stack, class instances reside in the heap, and static values reside in static memory.

·       Scala provides a pre-defined App object with an implicit main method:

object test extends App:
   for(i <- 0 to 50) clock.tick
   println
("time = " + clock.cycle) // prints "time = 51"}

Singletons as utilities

We can think of a singleton as a utility (aka service). A utility is an object that represents a library of related functions and values.

For example:

object conversions {
   val distanceFactor = 0.6214
   val weightFactor = 2.2046
   def km2miles(kms: Double) = kms * distanceFactor
   def miles2kms(miles: Double) = miles / distanceFactor
   def kgs2pounds(kgs: Double) = kgs / weightFactor
   def pounds2kgs(pounds: Double) = pounds * weightFactor
   // etc.
}

Notes

·       In this example I'm using the original (but still supported) syntax for declaring singletons that uses curly braces rather than indentation for specifying scopes.

·       In Java a utility is usually implemented as a class in which all of the members are static.

·       We can think of a utility as an object that represents a library.

·       We could have declared conversions to be a class, but then we would need to instantiate it before we could use it:

class conversions { ... }
val c1 = new conversions()
val c2 = new conversions()
c1.km2miles(3.1) // = 1.89844
c2.km2miles(3.1) // = 1.89844

·       We say that a class or singleton is cohesive if its properties and methods are closely related. There are degrees of cohesiveness: coincidental < logical < temporal < procedural < informational. See here for more information.