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")

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",

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.


AlBlue said...

The foo_ is the 'setter function' for the variable; what it's saying is that there isn't a setter method (because it's a val) and so I'm sorry Dave, I'm afraid I can't do that.

The {foo="blah";foo} is the expression that gets created on the fly when you type in the assignment in the interpreter. The last 'foo' means 'return the value of foo' since in Scala, assignment is a statement, not an expression (which is why a=b=0 doesn't work).

By the way, you (or others) might like the introductory series I've put together:


Ahnfelt said...

var - a standard variable like you know it from most other imperative languages. You can assign to it.

val - a standard variable, except you can't assign to it.

def - a function (in your case without parameter parenthesis, which makes it more like a read-only property). It's reevaluated every time you use it.

Randin said...

How is your work with Lift? I see that you have not written about it in a few months and was wondering if you've looked at the latest versions or have you given up on it?

David Rupp said...

Hi, Randin. Thanks for checking in.

I haven't so much "given up" as "had to put it aside". I've been so busy with school (finished that in May) and my work, which is primarily Java-based, that I've just not had the time to do it justice.

I do find it interesting that Scala seems to be headed in the direction of Haskell and friends, and not attracting a lot of mindshare with Java programmers. Which is too bad, as I think there's a lot of benefit to be gained by adding Scala to the mix.