Alex headshot

AlBlue’s Blog

Macs, Modularity and More

Introduction to Scala – compiler, packages, objects and applications

2007, howto, scala

In the previous post, we covered the basics of Scala classes. This time, we're looking at the compiler, packages, objects and applications.

Each Scala class created corresponds to a Java class under the covers (and potentially a number of other inner classes). These scala classes use the same packaging mechanism as Java does for name-space partitioning (or OSGi import/exporting). However, unlike Java, the naming of the source files/packages doesn't have to correspond with the location of those files on disk; a single Scala source file can contain many public classes from many packages. Of course, you can still use one-class-per-file if you want; and if you're using an IDE (such as the Scala plugin for Eclipse) then it doesn't make much practical difference anyway.

To create a class in a package other than the default one, the package keyword is used to enclose one or more classes. For example, we can create a package shapes to contain a number of shape classes:

package shapes {
  class Square(val width:Double, val height:Double, val x:Double, val y:Double) { }
  class Circle(val radius:Double, val x:Double, val y:Double) { }
}

Note that packages can't be defined in the interpreter, so we have to use the compiler to compile this. Save it as shapes.scala in your favourite editor of choice, and then run the compiler:

scalac shapes.scala

The compiler is sensitive to the extension; change it to something else (e.g. shapes.scala.txt or shapes.s) and it stops working.

This generates a directory shapes along with its contents, Square.class and Circle.class. For the inquisitive, you can use javap to see what the generated code looks like:

$ javap -classpath . shapes.Square
Compiled from "shapes.scala"
public class shapes.Square extends java.lang.Object implements scala.ScalaObject {
    public shapes.Square(double, double, double, double);
    public double y();
    public double x();
    public double height();
    public double width();
    public int $tag();
}

There's also a scalap that displays much the same thing but interprets the meanings of the code and displays them as Scala types:

$ scalap -classpath . shapes.Square
package shapes;
class Square extends scala.AnyRef with scala.ScalaObject {
  final var width: scala.Double;
  final var height: scala.Double;
  final var x: scala.Double;
  final var y: scala.Double;
  def $tag(): scala.Int;
  def width(): scala.Double;
  def height(): scala.Double;
  def x(): scala.Double;
  def y(): scala.Double;
  def this(scala.Double, scala.Double, scala.Double, scala.Double): scala.Unit;
}

Now that we've got it compiled to a .class file, we can You can use this class in any Java program simply by adding it to the CLASSPATH, along with the dependency scala-library.jar. We could use new shapes.Scala(1,2,3,4) in a Java program as well as a Scala program.

So far so good; we can add methods to our existing Scala classes. However, each of these methods only works if there's an instance already to be able to invoke it. What of class methods?

In Scala, there's no concept of a static modifier. Partially, that's because 'static' doesn't imply sensible connotations in other languages (who decided that 'static' should mean 'class based'?) but also because Scala's language tries to be elegant in replacing cruft with new code styles. In this case, to represent class-based members as effectively instance members of a singleton object. In fact, that's how the Scala Math code works1:

package scala {
  object Math {
    val Pi: double = 3.141...
    val E:  double = 2.718...
    def max(a:double,b:double):double = ...
  }
}

This allows us to access the values and functions without needing a specific instance of Math; in fact, since it doesn't have a constructor, it's not possible to call new Math. Instead, we address it just using the object name Math, much like we would do in Java2:

package shapes {
  class Circle(val radius:Double, val x:Double, val y:Double) { 
    def area = radius * radius * Math.Pi
  }
}

In fact, if you were to run javap on the generated classes, you'd see that this time, you're generating a class comprising of static methods. At the moment, there's no way of generating a class containing both static and non-static methods directly in Scala; the closest you get is to have a (class,object) pair that emulates what you might do in Java. However, note that in Java, the instance methods and class methods are generally called and used in very different scenarios, and that classes (like Math and System) are generally placeholders for class methods because there's no concept of a top-level function type on the JVM. Although it's a restriction worth knowing about, it generally doesn't cause any problems.

In fact, it brings in to play an obvious class method that we've not talked about up until this point; the JVM's good old main method:

object HelloWorld {
  def main(args:Array[String]) {
    Console.println("Hello World");
  } 
}

Compile this (with scalac HelloWorld.scala) and you can then run it from the command line (with scala HelloWorld).

There's one final thing worth mentioning; there's a placeholder application that you can extend instead of having to write your own main method. Any code in the body is executed as the application starts up:

object HelloWorld extends Application {
  Console.println("Hello World");
}

There's even a handy flag that you can set (scala.time) that will print out how long your application ran for:3

$ scala HelloWorld
Hello World
$ scala -Dscala.time HelloWorld
Hello World
[total 33ms]

That brings us to the end of this post; in the next post, we'll be looking at case classes and pattern matching.

  1. Actually, scala.Math are mostly just def aliases to the java.lang.Math counterparts; they're shown here as examplars
  2. Except Java's PI is uppercase; Scala's is Pi. Easy as pie to remember, huh?
  3. This only works when you extends Application. If you're writing your own main, don't expect it to work.