[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Showing posts with label lift. Show all posts
Showing posts with label lift. Show all posts

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.

Monday, October 22, 2007

Fun With Scala: Things I'm Learning From Lift #2

Thanks to astute reader Ken (Author's Note: I have a reader! An astute one! Who knew?!), I learned something about a feature I was bragging about in my last post; namely, the ability to import members of a class. As it turns out, not only is this feature present in Java 5 -- see the documentation for static imports -- the Scala version of it is pretty much equivalent.

Ken points out that my example of lift importing the members of the net.liftweb.util.Helpers "class" maps exactly to the corresponding Java import. I waved my hands around the fact that Helpers is actually an object, which is, simultaneously and at the same time, Scala's way of denoting a singleton and providing static members (all of the members of an object are effectively static). But it turns out that the import syntax I was bragging about works precisely because Helpers is an object and not a class.

Here's some lame, contrived, forced-example, somewhat-minimal Scala code to demonstrate:

Mixee.scala

package bogus;

class Mixee {
def weird: String = {"Weird Al!"}
def twentySeven: String = {"27!"}
}


Mixer.scala

package bogus;

import Mixee._;

object Mixer {
def main(args: Array[String]): Unit = { Console.println(weird); Console.println(twentySeven) }
}

As described here, Mixer.scala will refuse to compile, with the error: "not found: value Mixee". This is because Mixee is defined as a class. Change class to object and it compiles (and runs) just fine.

Bonus Discovery: While researching this, I also learned that Eclipse with the Scala plugin is not as smart as it should be about building my bogus project. If I start with a clean compile (i.e., defining Mixee as an object), Eclipse is happy. If I then change the definition of Mixee from object to class, the error doesn't show up until I force a rebuild by selecting Project -> Clean... If I then change class back to object, the error goes away without my forcing the rebuild.

Anyone got a line on that? Ken? Anyone?

Anyway, just for the record, my purpose with these posts is not to bury Java, but to praise Scala and learn how it's different. I'll leave the determination of whether Scala is "better" than Java up to you, my readers. Both of you (hi, Mom!). ;-)

Tuesday, October 16, 2007

Fun With Scala: Things I'm Learning From Lift #1

So I'm starting somewhere around the very beginning with the Liftweb framework: LiftServlet.scala, which is in the net.liftweb.http package. This looks like Lift's answer to a Rails controller, or a Struts Action (Author's Note: probably with some caveats that I haven't found yet; (pleasant) comments welcome if I'm wrong about this).

Fun Thing #1: The very first import is
import javax.servlet.http.{HttpServlet, HttpServletRequest , 
HttpServletResponse, HttpSession}

How's that for interoperability with Java? I haven't gotten that far yet, but this tells me that whatever Lift uses for its request/response implementation is going to conform to the usual Java interfaces. And how do you like the syntax for this style of import? I like it a lot, because it lets you do the equivalent of:
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
...et cetera...

...on a single line (it's a single line in the original source; I've split it here for friendlier rendering). Without resorting to importing everything in the http package with a wildcard. This way you get to see exactly what you need. Yeah, I know a good Java IDE will manage the imports for you, so it's not as much of a pain as it used to be, but I like the compactness of this syntax. As it happens, the current version of the Scala plugin for Eclipse doesn't have the equivalent of "Organize Imports", so this syntax is definitely a boon.

Fun Thing #2: There's a syntax for importing not just classes, but members of a class. Like so:
import net.liftweb.util.Helpers._

This basically says "make everything in the Helpers class (actually it's a singleton object, but think of it as a class for now) available to this class without my having to qualify it with 'Helpers.' all the time".

For example, there's a bit of code in LiftServlet that goes like this:
val md = parseInternetDate(mod)

This makes it appear that parseInternetDate() is defined in the LiftServlet class. But it's not. It's defined in Helpers. If you're used to modules/mixins in Ruby, this should seem very familiar to you. If not, now might be a good time to get used to them, because they're way cool.

Fun Thing #3: No, the lack of semicolons at the end of the code samples is not a typo. The Semicolon is Dead! Long Live the Semicolon! No, scratch that -- Long Live Scala!

Sunday, October 14, 2007

Fun With Scala: Liftweb Getting Started

Job one: checking out and Eclipsify-ing lift.

The instructions at the CircleShare lift blog are still current up to a point, at least as far as the prerequisites are concerned. I'm running from trunk, so my source code checkout was a little different:
svn co http://liftweb.googlecode.com/svn/liftweb

After that, the available instructions need a little (ahem) updating.

For a quick look at the kinds of things you can do with lift, run mvn install in the liftweb (top level) directory to build everything. When that's done, cd sites/project_name, where project_name is one of example, hellolift, or skittr. From there, run mvn jetty:run to start a Jetty web server running on some port on localhost. At this writing, example and hellolift run on port 8888, while skittr runs on 8889. Don't ask me why.

You can also mvn install individual projects if you want, but I don't see any harm in building them all in one shot.

To Eclipsify, make sure you have the Scala Development Tools plugin installed, then run mvn eclipse:eclipse in the project you want to be able to work with in Eclipse. You can do this for the lift source code itself (under the liftweb/lift subdirectory), or any of the example projects (liftweb/sites/project_name where project_name is as above).

Happy Lifting!