X
Tech

The case for Scala

Programming languages are like screwdrivers for developers. One size does not fit all, so good programmers keep several in their tool belt.
Written by Ed Burnette, Contributor

Programming languages are like screwdrivers for developers. One size does not fit all, so good programmers keep several in their tool belt. The problem is, new languages are coming out all the time and you've only got so much room on your belt and in your schedule to learn them. In today's guest column, language guru David Biesack makes the case for Scala, a new language that takes the best ideas from object oriented and functional programming languages. As a member of the JSR 201 committee, David helped design new syntax features in Java 5. He was also a reviewer for the Java Language Specification (3rd edition), Java in a Nutshell, and Programming in Scala. --Ed

scala-logo.png
Java has matured nicely in the last dozen years. That merely means Java developers are familiar with its shortcomings, like the verbosity of generic type declarations, interfaces that cannot define default method implementations, its lack of closures, etc. Backward compatibility keeps it from evolving radically, and other dynamic/scripting languages like Ruby, Groovy, Python are drawing a large following because of their ease of use and flexible capabilities. In this environment, (www.scala-lang.org) Martin Odersky created Scala as a language which does for Java what many Java did for C: advance the older language (with great features of other languages) while stripping it of its cruftier bits. However, just as Java was not C, Scala is not Java with new features. An entirely new OO language that runs on the JVM and integrates smoothly with Java 5, Scala also stirs things up by adding:
  • functional programming. Functions are first class objects in Scala — they may be passed as parameters, stored in variables, and can create and return other functions. Scala also makes it easy to turn an object's method into a function (see the example below);
  • type inference, which allows you to omit many type annotations. Scala code often has the look and feel of scripting languages;
  • traits, which describe interfaces but which may also include implementation methods;
  • a more consistent type system, including fixes for some of Java's generic type flaws. For example, Arrays and Lists are both generic subtypes of the Seq trait;
  • flexible packages and imports, including the ability to rename imported features;
  • case classes which implement the necessary boilerplate of holder/structure classes and convenient constructors, but also provide powerful object pattern matching;
  • Replaces operators with methods and a flexible method invocation syntax: the operation a + b is really the function call a.+(b). However, the Scala compiler optimizes many such operations into single Java bytecodes;
  • direct language support for XML (similar to Groovy);

and many more common language "wish list" features, already implemented in a consistent and clean manner.

To get Scala, download the version for your system (2.7.2-final is the current stable release), then add <scala>/bin to your PATH. The environment variable JAVA_HOME should also point to an installed Java runtime environment on your system. The Scala toolset includes scalac (the compiler), fsc (a resident compiler which avoids slower startup costs), and scala, the Scala runtime. There are also plugins for Eclipse, IntelliJ IDEA, and NetBeans, as well as editing and interpreter modes for Emacs.

Even Groovier

The Wikipedia page for Groovy shows how to filter a list of strings, printing only those of 4 or fewer characters:

// Groovy:
["Rod", "Carlos", "Chris"].findAll{it.size()<= 4}.
each{println it}
The Scala code to select and print "Rod" is similar:
// Scala:
List("Rod","Carlos", "Chris").filter{_.length <= 4}.
foreach{println}
filter is a method on the Seq type (a List extends Seq) which accepts a function. This function applies the length method to the argument (denoted here with _ instead of it) and compares that to 4. The filter method returns another Seq and the foreach method on that Seq applies the println function to each value within it. The Scala system includes a read, eval, print loop interpreter which is a quick, interactive way to try things out in the Scala system. From a console, simply run scala without specifying a main class, and enter definitions and expressions:
$ scala
scala> val r = 1 to 11
r: Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
scala> println((r foldLeft 0) {_ + _})
66
scala> println((r foldLeft 1) {_ * _})
39916800
1 to 11 evaluates to a range of integers from 1 to 11. The expression (r foldLeft 0) {_ + _} performs a left fold operation on the range, seeding it with 0, and applying an anonymous function literal, {_ + _}, which adds two values. This expression computes (...(((0 + 1) + 2) + 3) ... + 11). The third expression multiples the values, using a seed of 1. You can also use scala to run scripts written in Scala. Put the three statements above into folds.scala and run the script:
# scala folds.scala
66
39916800
Types, inferred

Scala's type inference is at work here. Scala infers the type of List("Rod", "Carlos", "Chris") in the first example to be a a list of Strings with the type List[String]. Thus, the result of the filter method is a Seq[String] (a sequence of Strings that does not have to be a List; List extends Seq). Further, the predicate function's type is inferred to be String) => Boolean (that is, it is a function that accepts a single String parameter and returns a Boolean.) The foreach method operates on a Seq[String] and therefore accepts another function type, (String) => Unit. (Unit is similar to a void type.) Thus, whereas Groovy uses dynamic types (and therefore runtime type checks), Scala uses compile-time enforced strong typing, resulting in better performance while maintaining a script-like style.

Flexibility

The flexibility of Scala's syntax allows the creation of domain specific languages directly in Scala. For example, Scala's Combinator Parsing allows the expression of grammars which look remarkably like EBNF directly in Scala, without altering the language at all — it is all done with functional composition and well chosen operators. On a larger scale, Scala support packages and fine-grained control of program element visibility. Compiling to efficient Java bytecode, combined with Java interoperability, makes it suitable for enterprise computing.

HQ9+, Interpreted

Here is a longer Scala example: an interpreter for the esoteric HQ9+ language invented by Cliff Biffle. HQ9+ is a language in which some common programming language tasks are coded most concisely:

  1. The statement 'H' prints "Hello, world!" (demonstrates basic console output)
  2. The statement '9' prints the lyrics to 99 Bottles of Beer (this either celebrates beer, or demonstrates loops, depending on the day of the week)
  3. 'Q' performs a quine itself (nothing inappropriate here; a quine is a program that can print its own source)
  4. '+' increments the register (programs have to do some math, right? HQ9+ can increment a numeric register, but cannot print it. Remember, we said HQ9+ was an esoteric language! Comments in the source show how to add new statements to print the register value.)

A forgiving Scala implementation of a H9Q+ interpreter follows.

object HQ9Plus extends Application {
case class Env(program:String, register:Int) // runtime env
type Instruction = Env => Env // function of Env to Env
type Statement = Char
val interpreter = Map[Statement, Instruction](
'H'->{(env:Env) => println("Hello, world!"); env},
'Q'->{(env:Env) => println(env.program); env},
'+'->{(env:Env) => Env(env.program, env.register + 1)},
// '?'->{println(env.register);env}, // extend language!
'9'->{(env:Env) =>
def printn(n:int, s:String) =
if (n == 1) println("1 Bottle of beer" + s)
else        println(n + " Bottles of beer" + s)
for (n <- 99 to 1 by -1) {
printn(n, " on the wall,")
printn(n,  ".")
println( "Take one down, pass it around," )
printn(n-1, " on the wall.")
}
env}
)
def interpret(src: String, env:Env) =
(src foldLeft env) { (env:Env, st:Statement) =>
interpreter.get(st.toUpperCase) match {
case Some(instruction) => instruction(env)
case None => env
}
}
override def main(args: Array[String]) {// process cmd line
args.foreach{prog:String => interpret(prog,Env(prog,0)) }
}
}
Compile and run the HQ9+ interperter as follows:
 scalac HQ9Plus.scala            # compile the program
scala HQ9Plus H H+H+H? HQ9+     # run three different HQ9+ programs
This is not the simplest implementation. Rather, this implementation was chosen because it demonstrates some of Scala's functional programming ideas. A good exercise in learning Scala is to refer to the Scala API and the Scala Tutorial to decipher the above object definition, then manually trace what happens when the program is run with the two command line arguments, "H" and "HQ9+". Here are some helpful links: Map class, Map object, foldLeft method, Function1 trait, Option class, and Application trait.

A Three Hour Tour

davidbiesack.jpg
To find out more about Scala, you can take the Tour of Scala. Some fascinating technical papers on Scala's type system, pattern matching, Actors concurrency library, and more may be found at Scala Papers and Talks.

Scala is not just interesting, it is a very clean and productive language. I recommend you take it for a test drive - you are bound to learn something new.

David J. Biesack is a software developer at SAS.

Editorial standards