General musings on programming languages, and Java.

Saturday, December 29, 2007

Make Scala Your Language for 2008

Scala's a statically-typed language based on Java, but with features that make it comparable to Ruby, Groovy, Haskell, Python, Erlang and Smalltalk. It's pronounced "Skah-la", rather than "Skay-la", it has closures, gets rid of Java's controversial checked exceptions, and is almost perfectly interoperable with Java APIs.

There's an official book nearing completion (available now in PDF form), and some very clever people are using Scala (and some not so clever ones, like me). One of the core developers is called Lex Spoon, which has to be a plus for any language. Scala's been cooking, and by the time you finish reading this and procrastinating about whether to use it, it'll be ready.

It is one of those languages where boilerplate isn't welcome, yet is statically typed and supports both OOP and functional programming without blinking. Scala programs can use Java APIs effortlessly, and Scala turns out to be better than Java for testing Java code (despite some integration concerns raised by Ola Bini)!

So how can you use it? Obviously, install it, then you can launch its interpreter (you can use it as a scripting language or a regular compile-to-bytecode JVM language [though the difference is an elaborate illusion]). Here I'll launch the interpreter with the Google Translator API on the classpath:

$ wget -q
$ scala -classpath google-api-translate-java-0.26.jar
Welcome to Scala version 2.6.0-final.
Type in expressions to have them evaluated.
Type :help for more information.

scala> import{Language,Translate}
import{Language, Translate}

scala> import Translate.translate
import Translate.translate

scala> import Language.{ENGLISH,SPANISH}
import Language.{ENGLISH, SPANISH}

scala> translate("bastante facil",SPANISH,ENGLISH)
res0: java.lang.String = Fairly easy
So it's pretty handy for trying out APIs, even built-in ones. Let's look at replacing all backslashes in a String with forward slashes, presumably to insert the resulting code into our Java program.
scala> "blah\\blah\\".replaceAll("\\","/")
java.util.regex.PatternSyntaxException: Unexpected internal error near index 1
        at java.util.regex.Pattern.error(
        at java.util.regex.Pattern.compile(
        at java.util.regex.Pattern.(
        at java.util.regex.Pattern.compile(
        at java.lang.String.replaceAll(
        at .(:4)
scala> "blah\\blah\\".replaceAll("\\\\","/")
res2: java.lang.String = blah/blah/
As you can see it's useful for prototyping little bits of Java too. But Scala in this case has a better way, too. Strings delimited with """ do not need any escaping (and can be multiline):
scala> """blah\blah\""".replaceAll("""\\""","/")
res4: java.lang.String = blah/blah/
Now the only escaping necessary is what Java's regex implementation requires.

Scala's method call syntax can be used without punctuation in some cases. x.y(z) can be written x y z, and x.y() can be written x.y. It also has implicit conversions, so if you define a conversion from type X to type Y, it looks as though X has all Y's methods. The type Int has a conversion to RichInt, and RichInt has a to(Int) method, so I can do:

res0: Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
or even better:
scala> 1 to 10
res1: Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
The lack of punctuation makes some things really attractive:
scala> (1 to 10) ++ (20 to 30) map (_.doubleValue) map Math.sqrt filter (x => x-
x.intValue>0.5) map (x => x*x) map Math.round
res60: Seq[Long] = Array(3, 7, 8, 21, 22, 23, 24)
This code takes the range 1 to 10, the range 20 to 30, and concatenates them together, then gives another range with the same values as the first but as doubles, then another with the same values as the second, but square rooted, then it gives another range with only those square roots whose fractional parts are greater than 0.5, then another range with the remaining values squared, then another with rounded values of those.

You can read the code pretty much like a bash pipeline. Here's the same thing how I imagine a bash programmer would like it:

concat $(range 1 10) $(range 20 30) | map doubleValue | map Math.sqrt | filter x-x.intValue>0.5 | map x*x | map Math.round
As with bash, each new Range does not stomp over the memory of the previous one.

Let's insert the punctuation again to see how it looks with Java-style punctuation:

scala> =>
 x-x.intValue>0.5)).map((x => x*x)).map(Math.round)
Eek! I think it's safe to say we wouldn't write such elegant code so often if we had to write (and read) the punctuation!

This use of methods as if they were infix operators is really powerful; so powerful that it is used for what we normally call infix operators. 3+4 is just 3.+(4) (operator precedence rules are preserved though).

That's enough for now.

No comments:

Blog Archive

About Me

A salsa dancing, DJing programmer from Manchester, England.