Testing with ScalaTestProgramming Practice (CS109)Singleton and companion objectsCompiled Scala programs

Compiled Scala programs

We write programs in a high-level programming language like Scala, Java, C++, or C. A compiler translates the source code to object code (machine code).

For C and C++, it is customary to compile to native machine code. It can be executed directly by the processor. Native machine code is different for different processors, operating systems, and can depend on library versions. So a program compiled for Windows does not run on Mac OS, a program compiled for iOS does not run on Windows, and a program compiled for Mac OS 10.9 may not work on Mac OS 10.10.

Java and Scala are normally translated to object code for the JVM (Java virtual machine). A Java runtime environment is needed on the computer to execute the program. The exact same object code works on any system. JVM is heavily used on servers.

Until now we have written our Scala programs as Scala scripts. Scala scripts are meant for quick programming tasks. Every time you run a Scala script, it is compiled again and then executed.

For larger programs, it is better to write a Scala application. It can consist of many separate source files that can be compiled individually (so you only have to compile the source file that you have changed). And once it has been compiled, a Scala application starts much faster than a Scala script.

Compiling

Let's start with a small example. We make a source file point.scala:

class Point(val x: Int, val y: Int) {
  override def toString: String = 
    "[%d,%d]".format(x,y)
}

I've made new directory that only contains this file now:

$ dir
point.scala

I will now compile point.scala using the Scala compiler scalac:

$ scalac point.scala 
$ dir
Point.class  point.scala

As you can see, one new file has been created, namely Point.class. A file with extension class contains exactly one class definition for the JVM.

We can now use this class from the interactive Scala mode (or from a script):

$ scala
Welcome to Scala version 2.11.5 (OpenJDK 64-Bit Server VM, Java 1.7.0_75).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val p = new Point(7, 13)
p: Point = [7,13]

Note that I did not define the Point class in this interactive session. Scala finds it automatically: When it sees the word Point which is currently not defined, it looks for the definition of a class or object with name Point. Therefore it checks all directories on its class path for files with the name Point.class. Since it finds this file in the current directory, it loads the class definition from that file. Note that the source file point.scala is not used—we can delete it and still use the Point class from Point.class.

The fast compiler

You may have noticed that scalac is quite slow. The problem is not the compiler itself, but that it needs to load the entire Java runtime system with many libraries.

We can compile our projects faster by using the Scala compilation server. Simply call fsc ("fast scala compiler") instead of scalac:

$ fsc point.scala 

The first time you call fsc, it is also quite slow because it needs to load everything. But it then keeps the compiler in memory, so the next time you compile, it is much faster.

The Scala compilation server may get confused when you move to a different directory to compile files there. Or when you are compiling on a USB-stick or external hard drive, and you are trying to eject that drive (your computer may tell you that the drive cannot be removed because it is busy). The reason is that the Scala compilation server remembers the last directory and is still active on that directory. In that case, the solution is to shut down the compilation server:

$ fsc -shutdown
[Compile server exited]

Scala applications

The Java virtual machine (JVM) has a purely object-oriented architecture. This means:

In short, all data must be inside objects, all computation inside methods of these objects.

As we mentioned before, Scala scripts are exempted from these rules because the Scala system automatically generates a "main class" that contains our global variables and functions as fields and methods. This means that Scala scripts usually do not compile. Even the simple script hello.scala

println("Hello World")

cannot be compiled, because it contains code that is not inside a method of a class:

$ scalac hello.scala 
hello.scala:2: error: expected class or object definition
println("Hello World")
^
one error found

So a Scala application has to be packaged inside a class. This leads to a chicken and egg problem: To do any computation, we need an object so that we can put the computation in its methods. But how do we get an object? We can only call new inside a method, and we have no object yet whose methods we can call!

The solution, of course, is that we need a singleton object. A singleton object is constructed automatically the first time it is used. The JVM starts a program by calling the main(args: Array[String]) method of a singleton object. The object is automatically constructed when this method is called.

A minimal example of a Scala application is numbers.scala:

object NumberGame {
  var secret = 0
  var guess = -1
  
  def answerGuess() {
    if (guess == secret)
      println("You got it")
    else if (guess > secret)
      println("Too big")
    else if (guess < secret)
      println("Too small")
  }
  
  def main(args: Array[String]) {
    secret = (math.random * 100).toInt
    while (guess != secret) {
      print("Guess my number> ")
      guess = readInt()
      answerGuess()
    }
  }
}
We compile this file by saying:
scalac numbers.scala
After compilation—if there were no error messages—some new class files have been created in the current directory, in this case NumberGame.class and NumberGame$.class.

To run the application, we say

$ scala NumberGame
Guess my number> 7
Too small
Guess my number> 14
Too small
Guess my number> 60
Too big
Guess my number> 35
Too small
Guess my number> 50
Too small
Guess my number> 55
Too big
Guess my number> 53
Too small
Guess my number> 54
You got it

Note that to run a Scala application, you have to provide the name of a compiled object, not a filename. In our case we want to call the main method inside the NumberGame object, so we need to provide the object name NumberGame.

As an example of a slightly larger Scala application, have a look at the application version blackjack-app.scala of the blackjack game.

Multiple source files

A larger Scala application will consist of several source files that define a number of classes and singleton objects. At least one of these singleton objects must define a method main(args:Array[String]) returning Unit. This method is the starting point of the program. The program is started by saying scala T, where T is the name of the singleton with the main method.

As an example, let's complete a day calculator (as in the project). It consists of two source files:

We need to compile both source files:

$ fsc date7.scala 
$ fsc days.scala 

And now we can call the program through its singleton object:

$ scala Days 2015/03/20
2015/03/20 is a Friday
$ scala Days 2012/02/10
2012/02/10 is a Friday
$ scala Days 1992/03/21
1992/03/21 is a Saturday
$ scala Days 1995/12/01 2015/03/20
There are 7049 days between Friday, December 1, 1995 and Friday, March 20, 2015
$ scala Days 1995/12/01 + 100
Friday, December 1, 1995 + 100 days = Sunday, March 10, 1996
$ scala Days 2015/03/20 - 1000
Friday, March 20, 2015 - 1000 days = Saturday, June 23, 2012
Testing with ScalaTestProgramming Practice (CS109)Singleton and companion objectsCompiled Scala programs