Showing posts with label lift. Show all posts
Showing posts with label lift. Show all posts

Tuesday, February 5, 2008

lift 0.5, now with Blueprint CSS beauty, jQuery excellence, and JSON goodness

Folks,

I am pleased to announce lift version 0.5.

lift is an expressive and elegant framework for writing web applications. lift stresses the importance of security, maintainability, scalability and performance, while allowing for high levels of developer productivity.

Demo: http://demo.liftweb.net

New in version 0.5:


  • jlift JavaScript utilities and associated binder helpers

  • Blueprint CSS built in (version 0.6 of Blueprint CSS) so it can be served via /classpath

  • XML -> JavaScript bindings so that JSON -> XHTML rendering can be defined using Scala XML literals, but the resulting JavaScript code can be sent to the browser to do mass rendering of JSON objects

  • JSON support including: - S.buildJSONFunc - JSONHandler class - Lots of fancy JavaScript expression building

  • Parser Combinator Helper routines

  • ByList query term

  • Support for serving certain common files (e.g., jQuery, Blueprint CSS) right from the JAR/WAR file

  • MappedText and MapperFakeClob classes for storing very long text fields in the database




Changes include:

  • Revised the example site to use Blueprint CSS for style

  • When lift is running in test mode, the default form generators will insert lift:field_name attribute into the form fields to aid in testing

  • Added json2.js (and minified version) Enhanced the resource serving stuff so that: - The path is defined in LiftServlet and changable (defaults to "classpath") - Moved the resource location from the root to "toserve" (can be changed) to avoid any possible way of serving up classes - The white list is a Partial Function for more flexibility - The white list by default contains jquery.js and json.js - There's a re-writer to re-write the request into an actual file (so jquery.js gets re-written to jquery-1.2.2-min.js)

  • MappedField.toForm now returns a Can[NodeSeq ] so a field can be omitted from a form

  • Added LocInfo and LocInfoVal to SiteMap for storing CSS and other tidbits about each menu item

  • Fixed the 'Welcome to the your project' type-o

  • Further abstracted the whole logging thing. There's a default log4j setup that can be override The LiftLogger trait is made concrete in Log4JLogger, but the generic LiftLogger creation is done by function that can be modified in LogBoot: var loggerByName: String => LiftLogger = _logger var loggerByClass: Class => LiftLogger = _loggerCls One can insert a new logging system into lift by replacing the loggerByName and loggerByClass functions with appropriate functions that return a LiftLogger.

  • Extensive updates to the <lift:xxx /> tag mechanism that uses tag labels as snippets and support for adding arbitrary tag handling to lift.


The lift community has grown to over 400 people. There are a number of lift-based project in development including http://much4.us which makes use of lift's Comet, AJAX, and JSON support.

Thanks to the excellent, talented team of lift committers including Jorge Ortiz who was build-master for this release, Steve Jenson who keeps adding practical things to lift, and the rest of the lift team.

Please take a look at lift 0.5, join the lift community, and give us some feedback!

Thanks,

David

Monday, January 7, 2008

Announcing lift 0.4

Folks,

The lift team is pleased to announce the release of version 0.4 of the lift web framework.

lift is an expressive and elegant open source framework for writing web applications. lift stresses the importance of security, maintainability, scalability and performance, while allowing for high levels of developer productivity.

lift borrows from the best of existing frameworks, providing

* Seaside's highly granular sessions and security
* Rails fast flash-to-bang
* Django's "more than just CRUD is included"
* Wicket's designer-friendly templating style (see Lift View First)

And because lift applications are written in Scala, an elegant new JVM language, you can still use your favorite Java libraries and deploy to your favorite Servlet Container. Use the code you've already written and deploy to the container you've already configured!

Version 0.4 marks a few major milestones for lift. It's the first time-based release we've done for lift. We're planning to do a monthly release between now and the 1.0 release of lift.

Version 0.4 also marks the first time folks other than me have made the largest contributions to lift during a release. Maciek's PostgreSQL support and DavidB's <head> rewrite contributions are amazing and important to lift. SteveJ continues his messaging rampage with XMPP support. Additionally, DavidB and Eric Torreborre (who has just joined the lift team) have been working on a test framework for lift.

So, please try lift 0.4, join the lift community and give us some feedback.

Thanks,

David

Changes in this version include:

New features:
o Added XMPP support for lift applications. see the package net.liftweb.xmpp
o Added support for switching XHTML MIME type serving off. See LiftServet.useXhtmlMimeType
o Adding support to H2 to lift-ORM
o Adding support to PostgreSQL to lift-ORM
o /html/body//head are merged into /html/head, to allow html fragment inlined into body to contribute to /html/head
o Adding a new lift archetype, lift-archetype-basic that will generate a lift template with basic database and users functionality.
o Adding LiftConsole to archetype to play with lift-based application in console mode
o Adding mock for HTTPRequest and Co
o Adding first Specs test to test lift-core


Changes:
o Upgrade to scala-2.6.1
o Support for PosgreSQL 8.1 and some tests
o The look of the maven site generated (for lift itself and for archetypes)

Removed:
o Removing deprecated new_proj (replaced by archetypes)
o Removing lift-archetype-hellolift (replaced by lift-archetype-basic (and the sample application hellolift))

Friday, December 7, 2007

Announcing lift 0.3.0

I'm am very pleased to announce that the lift web framework version 0.3.0 is now available.

lift is a secure, simple, powerful web framework for building real-time collaborative applications. lift is written in Scala, the functional/OO hybrid language that runs on the JVM and is completely interoperable with Java and Java libraries. lift applications deploy as WAR files on servlet engines supporting Servlet 2.4 and greater including Jetty and Tomcat.

First... some thanks... then the new features... then some goodies...

I'd like to thank the amazingly awesome lift committers... a growing team of extraordinary people. Since 0.2.0, lift has gone from being a near solo effort to being a group effort. David Bernard has done an awesome job of adding process, mainly in the form of Maven, to lift. Steve Jenson, Jorge Ortiz Hinojosa, and Viktor Klang have committed new features and enhancements into the code base. And Daniel Green, the lift outreach guy, has kept us excited and started the drum-beat to tell the world about the benefits of using lift. I'd also like to thank Gregory Brown of Ruport fame for showing me how to build an open source community.

New features:


  • Ajax support including jQuery integration
  • Radically improved Comet support
  • Facebook API support
  • AMQP (RabbitMQ) support
  • Built in traits (mixins) for Users and Tags
  • Support for new O-R mapped field types including Country, Locale, Time Zone, Postal Code, etc.
  • Lots of new example code including a real-time blog
  • Improved XHTML support for modern browsers
  • Support for multiple database instances
  • Improved regular expression support including regex -> object creation for simple parsers
  • A new and improved Textile parser
  • Much more documentation, both in the form of ScalaDocs and in the form of an wiki with lots of excellent code
  • While the new feature list is impressive, what is more important is that the lift APIs are settling and lift is becoming ready to build real applications.


There are half a dozen projects currently being built on lift and Much4 has been up and running and helping me organize my life.

So... the good stuff:

  • There's a new Scala Blog. It's a central point for lift and Scala related blogging including an excellent example of building Digg in < 140 lines of code
  • The "Programming in Scala" book will be available in draft version very soon
  • A video of me presenting on lift to the Seattle Java Users Group is available
  • The lift wiki has lots of budding tutorials, documentation, and frequently asked questions, etc.
  • The lift mailing list hit 600 posts last month and is now a "medium volume" Google Group
  • And... Daniel Green is the lift outreach guy (yeah, I said it already... but it's worth saying again.)


Please download Maven and Java... that's all you need to build a lift application. Type:

mvn archetype:create -U \
-DarchetypeGroupId=net.liftweb \
-DarchetypeArtifactId=lift-archetype-blank \
-DarchetypeVersion=0.3.0 \
-DremoteRepositories=http://scala-tools.org/repo-releases \
-DgroupId=com.test -DartifactId=ilikeit

And that's all there is to starting out with lift. If you like it... join the mailing list, ask questions, give feedback... If you don't like it... give feedback.

Thanks,

David

Wednesday, December 5, 2007

Dynamic web applications with lift and Scala

This is a tutorial introduction to building dynamic web applications with lift and Scala. We'll be building a linksharing site, much like Digg or reddit. However, lift's advanced support for Comet-enabled web applications will allow our site to update live as new links get submitted or old links get voted up or down, without our visitors having to refresh the page.



This is a short (50 seconds) screencast of what we're trying to build. The quality on the YouTube version isn't great, so you can also download the full-quality QuickTime version (~800K). I'm running two different browsers, Safari and Camino, and loading the finished site with both of them. Notice how the link submissions and votes made in one browser get automatically updated in the other browser. We accomplish this using Comet, and we'll see how lift makes Comet-enabled web applications a breeze. Let's get started.

I'll assume that you have the most recent versions of Scala, Eclipse, the Scala Plugin for Eclipse, and Maven installed. I'll also assume you have some rudimentary knowledge of Scala.

(I've you have used lift before the 0.3.0 release on Dec. 7, there is a bug in our repository that might cause you to use a pre-release version of the lift libraries. To fix this problem, delete the net/liftweb directory in your local Maven repository before continuing. I believe the default location is: ~/.m2/repository/net/liftweb)

From Eclipse's workspace directory, lets create a new project called "linkshare" in the "com.test" package by typing the following into the command line:

mvn org.apache.maven.plugins:maven-archetype-plugin:1.0-alpha-7:create -U \
-DarchetypeGroupId=net.liftweb \
-DarchetypeArtifactId=lift-archetype-blank \
-DarchetypeVersion=0.3.0 \
-DremoteRepositories=http://scala-tools.org/repo-releases \
-DgroupId=com.test -DartifactId=linkshare

That's all we need to do to set up a skeleton lift site. (If you're using Eclipse and want to use Q4E, see: http://liftweb.net/index.php/Archetypes) Now we'll go into the directory that contains our project and run it with Jetty. Maven should automatically download Jetty for you if you don't already have it.

cd linkshare
mvn jetty:run

If everything goes well, you should be able to see your skeleton lift site running on http://localhost:8080/. We see a nice welcome screen that shows the current time. Now let's do something with the site. Leave Jetty running, and open the index.html file in src/main/webapp. Edit the file so that it contains a more topical message, like "Welcome to your linksharing site!". Save the file and reload your browser. Your changes should have been picked up by Jetty.

Jetty also automatically picks up on changes to your compiled class files and redeploys your webapp when necessary, so you can leave the Jetty process running throughout this tutorial and see the changes in your browser. We'll still want to do some stuff on the command line, so open up another command line window. Then create an eclipse project with the following command:

mvn eclipse:eclipse

Now you can import the lift project into Eclipse. There are two directories you should mostly be concerned with. The src/main/scala directory contains all the Scala code for your lift project, while the src/main/webapp directory contains the html, css, javascript, etc.

You'll probably want to enable Eclipse's incremental compilation. That way, whenever you save changes to your code, Eclipse will recompile your .class files, and Jetty will pick up the new code. If you're not using Eclipse, or not using incremental compilation, you'll want to issue a 'mvn compile' command to see any changes to your code reflected in Jetty.

You'll notice that our app's page has a "Home" link which points right back to the main page. This is actually a part of lift's advanced Sitemap feature, but for now we won't be using it, so let's disable it.

The Sitemap is defined in our bootstrap. Our site's bootstrap code gets run by lift as soon as our app gets deployed. This is a great place to make sure we set up our databases correctly, among other things. It's also where our Sitemap is defined, so let's go take a look.

You'll find the bootstrap code in src/main/scala/bootstrap/liftweb/Boot.scala. It's fairly lonely in here, as our app doesn't do much yet. Let's modify the appropriate line to hide the "Home" link from our Sitemap. We do this by adding the "Hidden" argument, like so:

    val entries = Menu(Loc("Home", "/", "Home", Hidden)) :: Nil

Save the file, then wait a few seconds for Jetty to pick up the changes. (If you're not using Eclipse's incremental compilation, then you'll have to issue a 'mvn install' command.) When you refresh your site's page, you should see that the "Home" link has disappeared. Awesome! Now let's do something useful.

Our first order of business is to allow our visitors to submit links. If we want to receive links from our visitors, then we also want somewhere to keep those links. At this point, most people would start defining database connections, database schemata, model objects, object-relational mapping... Doing all of this is very easy with lift. The included Mapper classes provide a great way of easily, securely, and meaningfully storing your bytes.

However, I'm going to make a controversial decision: we won't use a database. lift, you see, is a highly stateful web framework. It keeps state around between requests, so we can get very far without a database. Of course, this means that if our app gets redeployed, or if it goes down for maintenance, or if it crashes, then we'll lose all of our state and we'll have to start collecting links from scratch. This is clearly unacceptable in a production environment, where we would want to persist our important information to a database. Like I said, lift makes this easy with the included Mapper classes. However, for the purposes of this demo, we can deal with losing all of our links every now and then.

So if we're not keeping our links in a database, then where are we keeping them? We're going to use a long-lived Scala Actor. Scala Actors are lightweight threads, similar to Erlang's Actors. They can send and receive messages to other Actors, which will come in very handy. Let's make a LinkStore object in our com.test.controller package:

package com.test.controller;

import scala.collection.mutable.{HashMap, HashSet}
import scala.actors.Actor
import scala.actors.Actor._

import net.liftweb.util.Helpers._

// Messages
case class AddListener(listener: Actor)
case class RemoveListener(listener: Actor)
case class UpdateLinks(topLinks: List[Link])
case class AddLink(url: String, title: String)
case class VoteUp(linkId: String)
case class VoteDown(linkId: String)

// Data Structures
case class Link(id: String, entry: LinkEntry)
case class LinkEntry(url: String, title: String, var score: Int)

object LinkStore extends Actor {
val linkMap = new HashMap[String, LinkEntry]
val listeners = new HashSet[Actor]
var topLinks: List[Link] = Nil

def notifyListeners = {
topLinks = linkMap.toList.map(p => Link(p._1, p._2)).
sort(_.entry.score > _.entry.score).take(20)

listeners.foreach(_ ! UpdateLinks(topLinks))
}

def act = {
loop {
react {
case AddListener(listener: Actor) =>
listeners.incl(listener)
reply(UpdateLinks(topLinks))
case RemoveListener(listener: Actor) =>
listeners.excl(listener)
case AddLink(url: String, title: String) =>
linkMap += randomString(12) -> LinkEntry(url, title, 1)
notifyListeners
case VoteUp(linkId: String) => try {
linkMap(linkId).score += 1
notifyListeners
}
case VoteDown(linkId: String) => try {
linkMap(linkId).score -= 1
notifyListeners
}
}
}
}

start // This starts our singleton Actor
}


The central data structure here is the LinkEntry. It stores the url, title, and current score of a link. Our LinkStore is a singleton Actor which is started up when the class is loaded. The LinkStore keeps a HashMap of LinkEntries, indexed by a unique ID of 12 characters. The LinkStore responds to three messages that can modify the LinkEntries: AddLink, VoteUp, and VoteDown. Each does what its name suggests. The LinkStore also keeps a HashSet of listeners. Listeners are other actors that want to be notified when the LinkStore changes. Registered listeners are sent the newest list of Top 20 Links whenever there is an update.

Now that we have a place to store our links, we want to let our visitors send them to us. If you take a look at index.html in src/main/webapp, you'll notice several <lift:...> tags. When a user requests a page, lift will try to find it in your src/main/webapp folder. If the page has any <lift:...> tags, they'll get processed before the response is sent back to the user. The first such tag we see is <lift:surround>. This tells lift to surround the enclosed xhtml with the "default" template. Templates can be found in src/main/webapp/templates-hidden. The second such tag is a <lift:snippet> tag. This tag instructs lift to call the "helloWorld:howdy" snippet, and replace the <lift:snippet> tag with the results of the snippet call. Snippets can be found in src/main/scala/com/test/snippet. Let's take a look at the HelloWorld.scala snippet. HelloWorld is a class which defines one method, "howdy". All that howdy does is return some xhtml. This is the xhtml that will replace the <lift:snippet> tag before the whole page gets sent back to the user. Notice that Scala allows XML literals right in the syntax. It also allows arbitrary Scala expressions inside the XML when escaped by {} brackets. As you can imagine, embedding xhtml straight into your code can be very useful for web development.

Let's get rid of the HelloWorld snippet and replace it with our own. Make a new class in com.test.snippet called "Submission", and define a method "form":

package com.test.snippet;

import net.liftweb.http.S
import net.liftweb.util.Helpers._

import com.test.controller._

class Submission {
def form = {
var url = ""
var title = ""

<span>
{ S.text("URL", u => url = u) }
{ S.text("Title", t => title = t) }
{ S.submit("Submit", ignore => LinkStore ! AddLink(url, title)) }
</span>
}
}


Phew! There's a lot going on in these few lines. Let's try to break it down piece by piece. We're taking full-advantage of Scala's support for XML in the syntax to send back an xhtml form. We're using {} brackets to escape the XML syntax and insert arbitrary Scala expressions. The first and second Scala expressions reference "S.text". This is a method that will return a text input field. The first argument to the method is the default text in the field. The second argument is a function that will be executed on the contents of the text input field when the form is submitted. Take a moment to let that sink in. So when the form gets submitted, the stuff in the url field will be assigned to the "url" variable and the stuff in the title field will be assigned to the "title" variable.

"But how?!", you might ask. After all, Submission's "form" method gets called in one request-response cycle, and the html form doesn't get submitted until the next request-response cycle! Well, this is part of lift's magic. Between requests, lift keeps around a closure that knows about the "url" variable and about the "title" variable. Even better, the closure knows what to do with these variables once the form submission comes in. It works almost like magic.

Which leaves us one last thing to analyze. As you might have guessed, "S.submit" returns a submit input field (a submit button). The first argument to "S.submit" is the label on the button. The second argument is a function to be called once the form is submitted. This function sends a message (using "!", a method defined on Actors) to the LinkStore, telling it that we want to add a new link.

Now let's go back to index.html and change the "helloWorld:howdy" snippet to a "submission:form" snippet, like so:

    <p><lift:snippet type="submission:form" form="post" /></p>

Notice that we added a "form" attribute to let lift know that this snippet should be rendered as a POST form.

Reload your page and you should now see two form fields and a submission button. Entering information and pressing submit doesn't seem to do anything, but if we add a "Console.println" to LinkStore we'll notice that it is indeed receiving and storing links.

Now that we have links, how do we share them with the world? Something needs to be responsible for showing these links to our visitors and updating them whenever necessary. This sounds like just the job for lift's CometActor. CometActors know how to render themselves onto an xhtml page. They are also long-lived; a visitor can navigate away from the page and come back to find the same CometActor servicing his requests. Since they're long-lived, they can interact asynchronously with the visitor, outside of the normal http request/response cycle. We'll use these capabilities to make "live" updates to the page a visitor is viewing, without the visitor having to refresh the page. Let's get cranking. Add a new "com.test.comet" package, and in it define a new LinkActor class:

package com.test.comet;

import scala.collection.mutable.HashMap
import scala.xml.{NodeSeq, Text}

import net.liftweb.http._
import net.liftweb.http.js.JsCmds.Noop
import net.liftweb.util.Can

import com.test.controller._

class LinkActor(theSession: LiftSession, name: Can[String],
defaultXml: NodeSeq, attributes: Map[String, String])
extends CometActor(theSession, name, defaultXml, attributes) {
var topLinks: List[Link] = Nil

def defaultPrefix = "links"

def render = {
def linkView(link: Link): NodeSeq = {
<li>
<a href={link.entry.url}>{link.entry.title}</a>
[{link.entry.score} {if (link.entry.score == 1) "vote" else "votes"}]
{S.a(() => {LinkStore ! VoteUp(link.id); Noop}, Text("[up]"))}
{S.a(() => {LinkStore ! VoteDown(link.id); Noop}, Text("[down]"))}
</li>
}

bind("view" -> <ol>{topLinks.flatMap(linkView _)}</ol>)
}

override def localSetup {
LinkStore !? AddListener(this) match {
case UpdateLinks(links) => topLinks = links
}
}

override def localShutdown {
LinkStore ! RemoveListener(this)
}

override def lowPriority : PartialFunction[Any, Unit] = {
case UpdateLinks(newLinks) => topLinks = newLinks; reRender(false)
}
}


Let's start with the easy stuff. The "localSetup" method is called when the CometActor is created. This method sends a message to LinkStore (to make sure it gets added as a listener) and waits for a reply. The reply will contain the initial list of top links, which we store in the "topLinks" variable. The "localShutdown" method is called when the CometActor gets retired. This just cleans up and makes sure we're no longer listening to the LinkStore.

The "render" method does most of the interest work. This is how the CometActor renders itself to xhtml. It's going to create some xhtml with an ordered list of links. The links themselves get rendered by the inner "linkView" method. This method will render the link with the appropriate title, as well as showing the score beside the link, and "[up]" and "[down]" links to vote the story up or down. The "S.a" method takes two parameters, and returns the xhtml code for an anchor tag (link). The first parameter is the code that will be executed when the link is pressed. This code sends a message to the LinkStore, telling it whether the vote was an up-vote or a down-vote. (The "Noop" at the end of the block is just to let Scala know that this is indeed a JsCmd block that is safe to send to the browser.) The second parameter is an xhtml node that will be wrapped inside the anchor tag. In this case, it's just a simple text node that indicates whether the vote is up or down. With these parameters, lift creates an anchor tag that, when clicked, will send an AJAX request to the server to execute the provided code. Neat stuff.

Finally, the "lowPriority" method is where we listen for updates from the LinkStore. As you might imagine, CometActors have three similar methods: "lowPriority", "mediumPriority", and "highPriority". The only differences are the order in which they get applied. We're not doing anything too complicated with CometActors here, so "lowPriority" will work fine for our purposes. Whenever we get an UpdateLinks message from the LinkStore, we want to update our local copy of the links. We also call "reRender", which pushes the latest result of "render" to the page containing the corresponding <lift:comet> tag for this CometActor.

The last thing we have to do is place the <lift:comet> tag on a page. This will render the CometActor we've just created to that page, and will listen to the CometActor for updates. Let's go back to index.html and add the following right below our form submission:

    <p>
<lift:comet type="LinkActor">
<links:view>Loading...</links:view>
</lift:comet>
</p>


Now you should have a site that functions exactly like the site in the screencast. If you open two different browsers, or two different windows in the same browser, actions taken in one browser window will be automatically reflected in the second browser window.

This introduction barely touches the full power of lift. We didn't really explore lift's Sitemap, ORM Mapper classes, extensive type-safety, semantic models, security-by-default, or advanced templating system. Hopefully, however, this gives you a taste of web development with lift, and showcases just one of lift's many features: advanced support for Comet-style web development.

This code borrows heavily from Steve Jenson's DynamicBlog, a part of the "hellolift" example project. Check it out at the lift source tree (http://liftweb.googlecode.com/svn/trunk/). Thanks to David Bernard for his help with syntax highlighting and with his feedback on a draft version of this blog post. And many, many thanks to David Pollak for his feedback on this blog post, but most of all for making such an awesome web framework.

Monday, December 3, 2007

A year with Scala and lift

I was a bit more than a year ago that I stumbled across Scala.

I had been doing Ruby and Rails for 18 months and Java for 10 years before I found Scala.

I was looking for a language that had the strengths of both Java and Ruby and I found them in Scala. I have been happy with the language and the community ever since.

Scala's goodness comes in many forms. Some practical (XML as part of the language syntax, runs on the JVM and has 100% compatibility with Java libraries), some geeky (Scala's type system is powerful and learning to program with primarily immutable data types is an excellent thought-challenge), and esoteric (Scala has an implementation of Erlang's Actors that's pretty spectacular.)

After coding in Scala for year, I find it very difficult to code in Ruby and almost impossible to code in Java. Scala has spoiled me on a bunch of different vectors.

Scala has a command-line interpreter. I can type lines of Scala, have them run... just like IRB in Ruby. That makes exploring a new API much, much easier. For example, I was writing some Crypto stuff the other day (in Java). I was able to check out the Java crypto stuff, run simple examples, etc. from the Scala command line. Once I was satisfied with the code, I ported it to Java and ran it. It's a lot easier than a "make changes to code, run the new program, see what it did" cycle with Java.

Scala's got XML right in the language. I was doing some SOAP coding recently. I captured the wire-level of some existing SOAP calls, copied the XML into my Scala code, and I had a running SOAP client in about 30 minutes. Some of the folks who were doing the same coding task in Java didn't even have all the import statements defined in by the time I had my code running.

Scala allows passing of functions and storing functions in Hashes, etc. It's like having amazingly light weight anonymous inner classes all over your code. In reality, the Scala compiler is creating a bunch of anonymous inner classes, but your code isn't cluttered with all the class definition stuff... your code is a simple statement that looks and feels like it's part of the current method. From a "picking your code up standpoint" it's much easier to understand what you did if your eye sees the substance rather than the cruft that is anonymous class definition.

You can code Scala just like you code Java. All the Java constructs are there (except the c-style for loop, but you'll never miss it.) You can connect to your Java classes and Java libraries from Scala. You can subclass Java classes, implement Java methods and your Scala code looks just like Java code to the other libraries. It's very simple to stick your toe in the Scala water and see if you like the temperature. Also, Scala code runs as fast as Java code so you're not sacrificing performance for language value.

Scala's traits and type system allow for type-safe, compiler checked meta-style programming. No, it's not changing the behavior of classes at runtime, but it is the ability to add complex functionality to classes simply by mixing new methods in. It's far more powerful than Ruby's mixin mechanism and the compiler takes care of making sure things don't get broken. An example of this is adding "user" functionality to an OR mapped class (first name, last name, email, password, etc.) In Ruby, this would be done with acts_as_xxx, in Scala this is done with class Foo extends Mapper with ProtoUser. And, yes, you can, at compile time, add methods to existing classes, even String.

So... what about lift?

One of the things that drove me to search for a new language after doing Ruby was the multi-fold needs for performance, type safety, and integration with Java libraries including Lucene.

I found Scala, but there were no web frameworks for Scala. Sure, I could have coded in Wicket and Hibernate using Scala, but then I would have lost a bunch of the Scala benefits. So, I sat down to write my own web framework. I started with a list of requirements and started coding.

I initially built lift "for me." Later, I started building lift for a multi-person team that I was part of.

Finally, the lift community developed a life of its own and people started using lift for real projects. There are currently 300+ people in the lift community, 5 active committers, and a bunch of active projects being built on lift (including Much4).

There are some amazing people in the lift community... people who know so much more than I do about so many different things. There's great give and take on the lift mailing list.

Most importantly, we're moving lift towards being a 1.0 product... a product that you can build a commercial application on top of. This means being developer friendly, having good documentation, having a decent tool chain.

So... what's so cool about lift?

It's got the code efficiency of Rails -- a few lines gets you a lot of stuff.

It's got the designer friendliness of Wicket.

It's got the statefulness of Wicket and Seaside.

Plus, lift is the best way to write AJAX and Comet applications. With a dozen lines, you can put a dynamically updating clock in your application. You can add chat with 50 lines. All this is possible because of Scala's Actors and the way lift abstracts away the HTTP request/response cycle.

Anyway... stay tuned to the Scala Blogs to see exciting developments in Scala and lift brought to you by some of the coolest people in the Scala and lift communities.