Thursday, November 01, 2007

Fun With Scala: Reading Scala

No, I haven't given up on my quest to learn more about Scala by perusing the guts of the liftweb source code. It's just that Iteration 3 at work, along with Hallowe'en with my two-year-old and Exam Two in Theory of Automata have all combined to kick my butt for the past couple of weeks. Not to mention all of the other life stuff that's intervening.

But no excuses! Let's have a look at some specific Scala code and see what we can figure out in general from it.

Today's snippet of Scala code is brought to you by net.liftweb.util.Helpers.scala, for those of you following along in the book. I've elided a lot of it, for the purposes of this post. Here are the relevant lines of code, suitable for entering into your scala interpreter:

import java.text.SimpleDateFormat
import java.util.TimeZone
val utc = TimeZone.getTimeZone("UTC")
def internetDateFormatter = {
val ret = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z")
ret.setTimeZone(utc)
ret
}

Besides the lack of semicolons (yea!) and the use of some standard Java classes (likewise yea!), one of the first things to notice is that utc is declared as a val, while internetDateFormatter is declared as a def. You can probably intuit the difference, but we'll see some examples in a moment of just how they're different. Also, notice that neither utc nor internetDateFormatter is given an explicit type. Each has a specific type, determined by the type of what's on the right hand of the assignment, but the compiler doesn't need to see the type declared on the left hand side. This has some consequences that we'll see too.

Finally, the definition of internetDateFormatter ends simply with the line ret. This idiom will be familiar to the Ruby programmers in the audience, as it simply declares what the ultimate value is of the block that constitutes the right hand side of the assignment to the variable internetDateFormatter. That is, in Scala, as in Ruby, the value of a block is the value of the last statement executed within the block.

Try entering the above code, line by line, in a scala interpreter. The imports aren't that interesting, but notice what you get as a result of entering the declaration of utc:

scala> val utc = TimeZone.getTimeZone("UTC")
utc: java.util.TimeZone = sun.util.calendar.ZoneInfo[id="UTC",
offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]

Which says that utc is of type java.util.TimeZone, and in particular it is equal to a sun.util.calendar.ZoneInfo with the corresponding id, offset, etc. Whereas when you finish declaring internetDateFormatter, you simply get:

scala> def internetDateFormatter = {
| val ret = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z")
| ret.setTimeZone(utc)
| ret
| }
internetDateFormatter: java.text.SimpleDateFormat

Now you can start using internetDateFormatter as you would any instance of SimpleDateFormat. For example:

scala> internetDateFormatter.toPattern()
res8: java.lang.String = EEE, d MMM yyyy HH:mm:ss z

Even more fun is this: now you have a result (res8, or whatever pops up for you), which is a java.lang.String, and can be treated as such:

scala> res8.split("\\s")
res9: Array[java.lang.String] = [Ljava.lang.String;@66718

scala> res9.toString()
res10: String = Array(EEE,, d, MMM, yyyy, HH:mm:ss, z)

(Author's Note: Have you ever wanted an irb (the interactive ruby interpreter) for Java? Download Scala, and get one for free today!)

So what about the difference between declaring with val and declaring with def? Let's find out:

scala> utc = TimeZone.getTimeZone("GMT-7")
line19$object.$iw.$iw.utc = TimeZone.getTimeZone("GMT-7")
:6: error: assignment to non-variable
val res12 = {utc = TimeZone.getTimeZone("GMT-7");utc}
^

Uh-oh. Looks like Scala considers vals to be constant. Although:

scala> val utc = TimeZone.getTimeZone("GMT-7")
utc: java.util.TimeZone = sun.util.calendar.ZoneInfo[id="GMT-07:00",offset=-25200000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]

It also looks like some constants are more constant than others. But before you start thinking you can just reassign internetDateFormatter with impunity:

scala> internetDateFormatter = "impunity"
:5: error: value internetDateFormatter_= is not a member of object $iw
val res13 = {internetDateFormatter = "impunity";internetDateFormatter}
^

Whatever that means. I'm going to have to track that one down and report on it later. Right now it's time for more life (read: "sleep") to intervene.