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.
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.
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]
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.scalaAfter 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.
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
Compiled Scala programs |