Lift has matured somewhat since then so I took another look. I'm still very much taken by the pitch; the framework has matured significantly and the documentation has improved beyond all recognition (i.e. there is now some - including an incomplete but nevertheless good online book. There are also tutorials starting to pop up, so I thought I'd take another look.
Setup is easy, making extensive use of Maven. Next up is creating domain classes - and I'm afraid the first disappointment. Per all web frameworks, Lift comes complete with its own Object Relational Mapping capabilty - named, appropriately, Mapper. The tutorial uses a "ToDo" class as an example, with each item having the following properties: a "done" flag (boolean), a priority value (Integer), a description (String) and a relationship to the Owner of this particular item.
Very simple. In Grails, I'd code this as:
class ToDo {
Boolean done
Integer priority
String description
User owner
}
Pretty intuitive, intentional and economic.
Here's the Lift equivalent from the Tutorial:
class ToDo extends LongKeyedMapper[ToDo] with IdPK {
def getSingleton = ToDo
object done extends MappedBoolean(this)
object owner extends MappedLongForeignKey(this, User)
object priority extends MappedInt(this) {
override def defaultValue = 5
}
object desc extends MappedPoliteString(this, 128)
}
object ToDo extends ToDo with LongKeyedMetaMapper[ToDo]
Wow! Now granted, this is my first introduction to Scala and Lift. But there seems to be an awful lot going on there:
- a class and an object with the same name;
- the main ToDo class extends a parameterised type - which is parameterised with ToDo itself
- a singleton ToDo which extends the ToDo class with another type parameterised with ToDo (the object or class? Not sure...)
- a slew of names evidently drawn from the world of relational databases (PK, ForeignKey, MappedXX types).
But even allowing for that, why does it have to be so complicated? From my perspective as a Scala and Lift newbie, it almost looks like a case of "we have all this cool stuff at our disposal, lets use it". Hence objects, classes, extension, traits and generics all make it in there.
Now I'm all for using the power of the language. But surely it should be used to both make things more intuitive and improve economy of expression? That certainly doesn't seem to be the case above.
Concept vs. Representation
Scala has some powerful XML handling capabilities, and Lift exploits these to enable tailoring of the way in which elements will be presented. Here's a snippet from one of the tutorials:
override def _toForm: Can[NodeSeq] = {
val funcName = S.mapFunc({s: List[String] => this.setFromAny(s)})
Full(</span><textarea style="font-family: courier new;" name="{funcName}" rows="{textareaRows.toString}" cols="{textareaCols.toString}" id="{fieldId}">{is.toString}</textarea><span style="font-family:courier new;">)
}
Powerful indeed. But wise? First off, it's mixing representation information with the domain class. Or, in MVC terms, mixing part of the View in with the Model. MVC says they should be separate, based on the principles of low coupling and high cohesion. It also complicates the workflow if a graphic designer is doing the UI / layout and a programmer doing the logic. As above, it seems like another case of wilful rather than judicious use of power.
And I'm afraid that's as far as I got. Maybe I should have persevered; Scala's Actor support holds significant promise for efficient scaling. But for now I'm not sold. Grails (and Groovy) may not have some of Scala's power features but the 'user interface' is significantly cleaner. So I'll continue grooving for now.