Laszlo

* - *
|

Put you in my calendar

I'm proud to say that Laszlo has shipped a preview of the calendar that Elastic Process has been developing for Laszlo's Webtop product. You can check it out yourself by registering for an account, but I also prepared this little demo to describe some of what's going on behind the scenes.

Laszlo Coding Demo

I recorded a screencast of the presentation I gave at AJAXWorld in March, and now it's posted on the OpenLaszlo blog. This demo shows an introduction to most of the basic features of OpenLaszlo, and it also uses the OpenLaszlo 4 DHTML output capability to integrate a Google Map.


Click To Play

I noticed that some people were requesting the source files. I fear that these may be lost to the ether, as I no longer have the machine I used to record the presentation.

Scripted Responses

A lot of people have been asking me lately how Laszlo compares to Ruby on Rails. RoR has done an excellent job of catching the Ajax wave, even though most of the renowned Ajax examples are written in Java and C. In spirit, Rails and Laszlo are kin: both fashion themselves as lightweight, novel ways to quickly create web-based applications. Both are kinds of responses to the options of heavier-weight Java on the one side, and the wild-west of PHP and Perl on the other.

Of course, there are some huge differences too. Rails focuses heavily on the server side of application building. It can generate a complete and functioning web app from a database schema. That probably won't be anything you'll want to ship, but it sure is an interesting way to get developers started. RoR addresses the client mostly as a renderer, much the way non-Ajax apps do. Of course, RoR does include some ballyhooed features that make it easier to deliver HTML snippets without reloading the page, but this is Ajax-defined-as a dash of MSG to liven up an old-school web app stew.

Now for whatever weaknesses RoR may have addressing the client, Laszlo has similar holes on the server side. But that's changing. Laszlo Mail of course has a substantial server component, and increasingly, our Studios customers are asking us to build middleware and do back-end integrations as parts of our projects for them.

Moreover, we've come to realize that the middleware for our apps, while often simple, is nearly always a custom bit of work that has its own idiosyncrasies. As an example of this, we've tended to shy away from implementations which rely on SOAP. This is only partly because SOAP is a bloated ugly mess; it's also because the thinking behind SOAP interfaces tends to come from the perspective of a class designer. The fact is that in most cases, networks still have observable latencies, and it's important to think about the server API in terms of the client's need for it, rather than in abstract domain terms. Some work has to go into crafting a fairly coarse interface that supports the client directly.

Now this is rarely rocket science, but there are a few gotchas. What if your dataset is too big to be efficiently transmitted to the client in one go? Well then the server can send it in pages, and the client can represent the dataset as a sparse array. Ok no problem, but then what happens when the user changes the sort order? Back to the server and the APIs to allow the client to pass in a sort order when it requests a data page. Sounds good, but now what happens when the user makes a selection and then changes the sort order? The user-friendly thing to do would be keep the selected record in view, even though it's at an unknowable point in the newly sorted result set. But that requires a different server API and some fairly substantial database trickery. We're all about user friendly here at Laszlo, but you can bet that we didn't get this one right in our 1.0 of Laszlo Mail. (It's fixed now.)

There's something worse, though, about having to develop the middleware for an active-client app (and here I'm drawing a distinction with server-side apps that just do tricks like loading a new div into an existing page.) When I've had to do this, the thing that has bothered me most is the replicated code that I've had to write for both the client and the server. The crux of this is validation. If I have a simple domain element like an address, I end up wanting to encode a bunch of information about what's in an address on the client. By this I mean complex information beyond the simple data type stuff that Rails will do. Given a country code, do I have a valid postal code, if one is even needed? Given a partial phone number associated with this address, how should this number be dialed depending on my location?

I may also want higher-level domain operations. I may want to tie a contact in my personal address book to a user in the system in a complex and active way, so that I can seamlessly integrate my data about a contact with the data that she publishes about herself.

If you've read any of my previous articles, you can probably guess where I'm going with this. The solution, to my mind, is to allow developers to express a single domain concept in a single, portable language that can run on both the client and the server: JavaScript. Now Ruby has that ruby-to-JavaScript thing, and Google released that bizarre bit of I-like-to-type webtoolkit but these are both cases where the client is still addressed a renderer -- a renderer of very simple domain logic, but not an active entity with its own notion of application state.

We've become so used to centering our applications around the server that David Temkin asserts that it's nothing short of a Copernican revolution to consider the client as the core of a given application. To me, it's funny to think of this as revolutionary. Having built a bunch of these apps, it's clear that the server-side is just a part of the app development equation. It's also clear to me that the Web 2.0 fad is all about user interface, and that the extent to which the client app, the part closest to the user, is in control of the experience is the extent to which it will succeed as a user experience.

The standard story around this kind of integration is that you use a popular MVC framework like JSF or struts to present your XML API, and that you write your Laszlo client to consume the XML data that results from your requests. I'm guilty of touting this myself. But without exception, the server-side frameworks are all much too heavy to justify the cost of integrating them. There's no need for complex rendering paths in XML APIs. No the truth is that the best way to implement a Laszlo middle-tier is to make it as abstract as possible. You don't want to be tied to a particular framework, or even a particular transport. The strategy is to build a network of simple objects, related in simple ways, that can be run in a single thread to provide stateless responses to an incoming request.

Truth is, most of the server-side frameworks are really about supporting the increasingly elaborate and idiosyncratic needs of the browsers. When the client is doing more of the heavy lifting, the server part becomes an exercise in almost pure class-design. (Of course, such exercises are exactly how you end up making absolutely everything an object (beware transactionEnvironmentHandoffMediator.)) The rendering of an object as XML is trivial, and it certainly doesn't require any elaborate view-templating system.

So you can imagine that we're working on extending Laszlo to the server. Thanks to our partnership with Dojo, we already have the makings of a Rhino runtime. Rhino is powerful stuff. It allows full reflection over regular Java objects and compiles into fairly efficient Java byte code. As a language, JavaScript isn't nearly as obtuse as Ruby, but its flexibility makes it easy to configure classes at runtime, which is exactly what you need when you're auto-generating models from database schemas, but you want to avoid icky code generation.

To draw another distinction with Flex, the folks at Macrobe seem to be working hard at at perfecting the Ptolemaic System: ever more numerous data integration points, with ever more complicated and enterprise-y plumbing. They're tuning their equants and epicycles, all to better wedge their apps into Web 1.0 architecture, while here at Laszlo we're eccentric like my man Jo Kepler.

Our Feature Presentation

Laszlo has a bunch of nifty databinding features. A lot of them are fairly high-level and quite powerful, but I've noticed (and experienced) that there are a lot of cases that they don't quite cover. It's been interesting to be involved in designing APIs and then to see how they are used in the wild. Well designed parts of the API not only flourish -- they become support for increasing levels of elegant and useful abstraction.

I've written about some of these successes and failures before, but with some additional perspective, I've realized that a well designed API is a like a good novel. In a good book, the story that you read is the tip of an iceberg that sits atop a carefully thought-through milieu of characters and situations. Even if you, as a reader, never untangle the various threads of the plot backstory you sense its solidity; the characters take on weight that extends beyond their actions in the plot.

A good API has that same feeling of solidity. It may present a fairly high level of abstraction, but as a user of it, you sense its cohesion and its representation of something integral and fundamental. A bad API, like a bad novel, feels like a trick: key information is withheld and simple relationships are hard to deduce.

The Laszlo data replication stuff is more like the latter. The entry points for sorting are hard to understand, and developers are forever confused about how to go back and get the view which represents a piece of data.

In our painfully slow but ever-striving process of self-improvement, we introduced a small method in OpenLaszlo 3.0 that gets developers closer to the truth of the matter: data replication is essentially a controller which iterates over a list. Before OL3, this list could only be treated as an abstraction: the set of nodes that is selected using an XPath query.

It turns out that there was a perfect entry point for this already in the LFC, and all that we needed to do was expose it in the right way. Of course, that last bit was non-trivial, and we had been stewing on the right design for a few releases. Here's an example of the basic replication feature, as it can be used to represent a simple and arbitrary list of vegetables:

<canvas proxied="false">
    <dataset name="dsVeggies" src="vegetables.xml"/>

    <window width="300" height="300">
        <view>
            <text width="300">
                <datapath xpath="dsVeggies:/vegetables/*/text()"
                          sortpath="text()"
                          replication="lazy"/>
            </text>
        </view>
        <scrollbar/>
    </window>
</canvas>

Classic question: how do I add sorting to this? Ok well it's not so bad, you just have to define a sortpath, which is an xpath expression that returns a key that you can sort on.

        <datapath xpath="dsVeggies:/vegetables/*/text()"
                  sortpath="text()"
                  replication="lazy"/>

And we get an app that looks like this: (Source)


Of course, it's a little strange that we have to repeat the text() operator, but let's just assume that we can live with that for now. Ok, works well for a very simple case, but it's hard to imagine exactly where that sort got applied. Is the original list now sorted? (No.) If I add an item to the original list, does it just get inserted or does the whole list have to be re-sorted? (Sadly, the latter.)

It quickly gets harder, though, when we decide that we want to sort on two keys.Like any reasonable person, I want to put the icky vegetables last -- they're marked as such with an icky="true" attribute. This can be accomplished by using a sortpath of "." and defining a sort function, but these APIs are a little confusing. Wouldn't it be better if we could just come up with the list of nodes we want to represent and then hand it to a replication manager?

Well the good news is that now we can. The setNodes API allows for this. First we grab the list of nodes, and then we sort them however we like, using ECMA's built-in Array.sort method.

<canvas proxied="false" height="200">
    <dataset name="dsVeggies" src="vegetables.xml"/>

    <script>
        <![CDATA[
        function sortEm( a, b ){
            var aicky = a.getAttr( 'icky' );
            var bicky = b.getAttr( 'icky' );
            if ( aicky == bicky ){
                return a.getFirstChild().data < b.getFirstChild().data ?
                       -1 : 1;
            } else if ( aicky == "true" ){
                return 1;
            }
            return -1;
        }
        ]]>
    </script>

    <method event="oninit">
        var list = dsVeggies.getFirstChild().childNodes.concat();
        list.sort( sortEm );
        gReplicated.datapath.setNodes( list );
    </method>

    <window title="Fruits and Vegetables" width="300" height="200">
        <view>
            <text id="gReplicated" width="300">
                <datapath xpath="text()" replication="lazy"/>
            </text>
        </view>
        <scrollbar/>
    </window>
</canvas>

There we go. Lima beans at the bottom, where they belong.

The other nice thing about this API is that now we have direct control over the list. We can add a method to our list manager that lets us remove an element in the list without having to muck with the data. As a side benefit, these types of operations can be much more efficient than using the xpath operators and complicating the client-side data with additional markup about how it's being displayed. (Source)

<canvas proxied="false" height="200">
   ....
    <node name="gListMan">
        <attribute name="list"/>

        <method name="reset" event="oninit">
            this.list = dsVeggies.getFirstChild().childNodes;
            list.sort( sortEm );
            this.updateList();
        </method>

        <method name="removeElement" args="el">
            for ( var i in list ){
                if ( list[ i ] == el ){
                    list.splice( i , 1 );
                    this.updateList();
                    return;
                }
            }
        </method>

        <method name="updateList">
            gReplicated.datapath.setNodes( list );
        </method>

    </node>
    ....
            <text id="gReplicated" width="300" ...
                   onclick="gListMan.removeElement( datapath.p )">
                <datapath xpath="text()" replication="lazy"/>
            </text>
            ....
    <button onclick="gListMan.reset()" x="320">Reset

Now by clicking on elements in the list, the user can remove them, all without altering the original data. Hopefully, it's clear from this example how this feature could be used to implement things like a quick search filter.

Player Hater

I found this reaction to Laszlo interesting.

This is clearly a reasonably nuanced criticism, and I'm always
interested to hear about what people make of the work we've done. If
you've read the post, then you probably know that this fellow didn't
think much of it, and that there are at least a few others out there
who agree with him.

I have to admit that I'm sympathetic to many of the points that the
post makes. They're valid, and no one is more aware of the
shortcomings of the product than those of us who are closest to it.
That said, I can't help but argue the opposite side :)

The post makes two main points about Laszlo. The first is that
Laszlo has bugs and missing features. The second is that the
architecture of a Laszlo app is different from that of a
conventional server-side application.

The first point must be conceded. There are a lot of bugs in Laszlo.
That said, there are a lot of bugs and missing features in other
software that's become very popular. Laszlo is a fairly complicated
system -- think of it like an OS for web-based applications. As
such, I think it's surprisingly correct and complete. The post
evaluates the 2.x version of Laszlo's product, and some of the
issues mentioned are actually addressed by Laszlo 3.x. That said,
doesn't everyone remember how horrible it was to code for Windows
3.0? Still, it provided utility and allowed developers to create
graphical user interfaces more quickly, and it caught on despite
some of the glaring problems with its APIs and component set.

So sure, some higher level concepts such as complex drag/drop
management and data transaction rollback are not built into
the platform or the core component set. What makes Laszlo
interesting to me is that it's so easy to write this stuff. When my
group, Laszlo Studios, starts a new development
project, we often begin with objects that provide this kind of
functionality. What's cool is that they're so simple that we
can quickly build and customize these pieces for each individual
solution.

The other thing that's cool is that the product is
open source
. People can write these things and contribute them
back to the community. Our hope is that passionate and slightly
irritated people like this poster will just go out fix some of the
bugs and create some of the missing features. There's a lot
of work to do, and we're expecting that there are a lot of people
out there who want to help
us do it.

As for the second point, that extra work must done to implement some
features on both the server and the client for a Laszlo app, I think
that there is limited truth to this assertion. The fact is that we develop most of our Laszlo applications in less time than it takes to do a similar Java/DHTML app. I have seen that developers run into problems when they don't fully make the switch to thinking about the client as a fully formed MVC environment, and of the server only as a data and business logic access layer.

I can see how people may have gotten the wrong impression. This
article
about a different architecture for Laszlo apps contains
some scary looking architecture diagrams. I have tried to show that
in practice the backends
for these apps are fairly simple
. I also think that the growing
consensus on XML-REST
being the best way to provide these
backends supports this simple model of client/server development.

I'm sure that some people will be deterred by cricism like this. My
hope is that a lot of other people will be inspired by all of the
opportunites there are to evolve the only free, open platform for
next generation web applications.

Strut This Way

Oliver Steele posted this excellent article on his blog about the differences between the architecture for a conventional server-side HTML application and that of a rich internet app. Oliver ends up espousing a Service-Oriented Architecture, which is a recieved idea about how to architect network-based applications. Oliver's architecture diagrams are beautiful, but I'm more of a learn-by-example type (Software Architect vs. Client Programmer?) so I wanted to use a practical example that shows how an existing webapp would be modified to incorporate Laszlo.

Most server-side developers who are new to Laszlo have a single, over-riding question, which has a few variants: "How does Laszlo work with my existing web application framework (usually Struts)?" You can see this again and again and again on the Laszlo forums. I don't feel strongly about Struts one way or another, so I don't wish to treat in detail the questions about whether Struts is the right framework for doing MVC in a webapp. The fact is that while many developers aren't using Struts, they have usually built some kind of framework for separating actions and views, because that relationship is so tortured in HTML. The swirl of issues usually boils down to these two or three questions:

  1. How do I embed a Laszlo app into a larger HTML-based application flow?
  2. and

  3. How do I get the data I have on the server into Laszlo?
  4. and sometimes

  5. How do I get some of that good ol' model/view separation within Laszlo?

The answer to these questions is the subject of this entry. I'd like to show in practical terms exactly how to adapt an existing web application to use Laszlo. I'm going to do all of this without using Struts (or, in fact, any MVC framework) but hopefully you'll see how this is relevant to a more complex example.

Let's start with a small webapp. This is my photoviewer, and it is composed of three extremely simple jsps. You can try the webapp here:



In this simple example, login.jsp presents a simple form, whose submission is forwarded to album.jsp. album.jsp then looks for a username passed to it, and if found, stores the username and the user's album in the session. If you look at the code, you'll note that the user identity is not authenticated, and that everyone gets the same album, but hopefully you'll get the idea. Once the album is retrieved, it is stored, along with the user identity, in the servlet session.

I should point out that this example does exactly what a framework like Struts prevents -- which is that login.jsp is hooked directly to album.jsp. We should have the action for the form on the login be more like /do/Login which then calls album.jsp on success -- but hopefully you see how this is relevant to a more complex example. I have worked around this problem by checking for null values passed to album.jsp in the username. If the value is null, the identity and album is retrieved from the session; if it is non-null, the authentication/data retreival code gets run. This allows me to use album.jsp to display the album whether I'm logging in for the first time or coming back to it from picture.jsp, but it's sloppy and fragile and all those J2EE MVC frameworks exist for good reason.

Anway, the picture album is represented in this code as a vector of HashMaps, but the particular implementation is unimportant. The assumption here is that you already have code which can pull database or other backend data into Java.

picture.jsp is similarly simple, and I've included it in the old app only to make a point about the new one. Once again, it retrieves the user name and album data from the session.

Now let's say that we want to employ Laszlo -- in this case to make better use of the extremely limited space for this little app. I'm not going to spend a lot of time on how the laszlo app is built, since I'm much more interested in how the laszlo app will work with the backend.

As a first step, let's leave everything else in tact, and turn the functionality in album.jsp into a laszlo app.

Something like this:



Not the most amazing demo, but at least you can immediately tell which parts are Laszlo. Let's take a second to break down the code.

I've had to rename all the files since all the links are written in to the jsps, but login1.jsp and picture1.jsp are the same, except that their links to the album are written as "album1.jsp".

Now, album1.jsp is simpler than album.jsp was -- its only responsibility is storing the username and album data in the session if it gets passed authentication information. Its other job is to embed the laszlo app, album1.lzx into the page. This is done by using Laszlo's lzEmbed call, which is a function that can write a Laszlo app into a page and is provided in a separate js file that is part of an LPS install.

In album1.jsp, the code that embeds the Laszlo control looks like this:

    <!-- include Laszlo's javascript library -->
    <script type="text/javascript" src="/lps-2.2.1/lps/includes/embed.js">
    </script>
    ...
    <script>
    <!-- Now call the lzEmbed function that's provided in the library-->
    lzEmbed({url: 'album1.lzx?lzt=swf', bgcolor: '#ffffff', width: '550', height: '300'});
    </script>

Again, I'm not going to dwell on the structure of the Laszlo app, which is trivial, but I want to focus on how the Laszlo gets data from the backend. This is done with albumxml.jsp.

This is the exciting part. albumxml.jsp is really simple, and it's just like a normal jsp. Let's take a look at how it works. First, there's this:

<%
    response.setContentType("text/xml");
%>

Which simple tells the requestor that the response will be XML and not HTML. Next, we retrieve the username from the session and send it as an attribute of the dataset:

<album user="<% out.print( session.getValue( "uname" ) ); %>">

Finally, we loop through the data in the album -- just as we did when we were rending for HTML -- but now emit XML, which is remarkably simpler:

    <%
        ArrayList album = ( ArrayList ) session.getValue( "album" );
        for ( int i= 0 ;i < album.size(); i++ ){
            String s = "<picture ";

            /*
            ...
            for ( properties in albumelements )
                s += properties +"='" + albumelements[ properties ] +"' "
                //like this prop='val'

            */

            s += "/>";
            //so s="<picture prop1='val1' ... propn='valn' />
            out.println( s );
        }
    %>

One last important note: Laszlo apps play in a Flash control hosted by the browser. When the Flash player makes HTTP requests, it uses the browser's mechansim, meaning that it sends all of the browser's cookies. This is what allows us to write code that relies on session data even for requests that are made from the Laszlo app. The result is an XML document that contains only the information we need. Its output looks like this.

So, to recap, we've answered the first question in the above list, which was about how we embed a Laszlo app into an HTML application flow. I should point out that a lot of people who think mainly in terms of server-side applications (which is a lot of people) often try to do something like this:

    <!-- BAD EXAMPLE!!! -->
    <!-- put laszlo app with data into a jsp -->
    <canvas width="500">
    <%
        //psuedo code
        for pix in album
            //code that renders laszlo, as if it were a jsp rendering HTML:
            String s = "<view resource='http:" + pix.get( "url" );
            //etc.
            out.print s + "/>";

    %>
    <!-- rest of program -->
    ...
    </canvas>

Often people get struck trying to figure out how to embed the Laszlo code directly into an HTML page. Sometimes they even get as far as figuring out that the jsp that renders Laszlo code has to be separate, with its output handed to the LPS compiler somehow. If they do get this far, they're disappointed with the results. You want to write a Laszlo app that loads dynamic data in a dataset. The app itself should be static. This is not only because Laszlo's compiler is currently dreadfully slow -- this scheme would never work in a high volume production setting -- it's also because it's usually a bad idea to have non-trivial code that writes non-trivial code. (Of course that's essentially what most modern J2EE/DHTML apps do these days, but that's the example that proves the rule.)

Hopefully, my liberal use of <em> tags has gotten my point across about dynamic data, so that answers question number 2 above, which was: how does the Laszlo app get data from the backend? Hopefully that's pretty clear by now:
you can use all the goodies that you're used to in the J2EE environment, including the request object, the session, and any other code that you have, to render XML for consumption by the front end. Of course, there are more exotic options out there such as SOAP and XML-RPC, but custom XML over HTTP is very easy to do, and most production Laszlo apps use this approach.

The last question on the list was about how to add something like an MVC controller to a Laszlo app. You probably noted that the last example above did not provide the best user experience -- especially since the picture details were presented on a separate HTML page. Here is a final take on this app, with all of the functionality moved into Laszlo.



Again, not the most impressive application, but for once we're not really focussed on the presentation layer. Instead, let's take a look at how the app changed to accomodate its move completely into the world of Laszlo.

First of all, we changed login.jsp from a form renderer to a responder which takes authentication info and sets up the session with user information and user data. Now it just looks like this:

<%
    response.setContentType("text/xml");
    session.putValue( "uname", (String) request.getParameter( "uname" ));
    session.putValue( "album", album );
%>
<ok/>

This doesn't really do much except store the username and album data in the session, but of course you could imagine that it actually authenticates and tells the Laszlo app whether there was success or failure. For now, it unconditionally sends the success token, and that in turn triggers the request for the album data. You could imagine that a more complex application would do the login step and then make separate backend data requests during its run depending on user actions.

The rest of the backend is the same, with albumxml.jsp still responding with the XML data stored in the backend Java object that's kept in the session. The part of interest here is that we have added a controller to the Laszlo app to help it manage its various states. The controller looks like this:

    <node name="controller">
        <attribute name="state" value="0"/>
        ...

The main thing of note here is that the controller exposes a property called "state", which represents the mode of the app. The various pieces of the UI have constraints on their visible property that look like this:

    <!-- login -->
    <view align="center" valign="middle" visible="${controller.state == 0 }">
    ...
    <!-- album -->
    <view datapath="dsAlbum:/album" width="100%" y="20"
          visible="${controller.state == 1 }">
    ...
    <!-- details -->
    <view visible="${controller.state == 2 }">
    ...

The controller also has several methods that various UI elements talk to in order to change the application state. It stores a reference to the data for the selected picture which the user clicks a thumbnail. Here's the whole thing:

    <node name="controller">
        <attribute name="state" value="0"/>
        <attribute name="picdata" value="null"/>
        <method name="didAuth" args="response">
            dsAlbum.doRequest();
        </method>
        <method name="gotAlbum">
            this.setAttribute( "state" , 1 );
        </method>
        <method name="showDetailFor" args="d">
            this.setAttribute( 'picdata' , d);
            this.setAttribute( "state" , 2 );
        </method>
        <method name="goBackToAlbum">
            this.setAttribute( "state" , 1 );
        </method>
    </node>

You can see the whole program here.

This is a fairly primitive example, but hopefully it begins the to answer the question of how control can be centralized in a Laszlo app. It's worth noting that in general, if you have a Laszlo app with a lot of screens, you probably haven't spent enough time thinking about how you can adapt your user-interface design from HTML to the kind of universal canvas/shared space approach that you can take in a Laszlo application. Still, most apps have some modal sense of state, and that's best handled with a controller like the one in this small application.

Performance Optimism

The Flash player executes bytecode slowly. You can see here that a simple function that adds one to a number takes a significant amount of time to execute.

<canvas height="100">
    <simplelayout/>

    <button>Test
        <method event="onclick">
            tester.doTest();
        </method>
    </button>
    <node name="tester">
        <attribute name="reps" value="50000"/>
        <method name="doTest">
            <![CDATA[
            var f = function ( a ){ return a + 1; }

            var t = new Date();
            for ( var i = reps; i >=0; i-- ){ f( i ); }
            t = ( new Date() ) - t;

            report.addText( ' ' + ( t/reps ) );
            ]]>
        </method>
    </node>

    <text name="report" resize="true">
        Avg function time:
    </text>

</canvas>

Compare this to the JavaScript interpreter in your browser. This is pretty much the same test, and your browser will show you that it takes significantly less time to execute. Depending on your browser/flash player combination this difference may be as much as 400x.


<script>
    function doTest(){
        var reps = 50000;
        var f = function ( a ){ return a + 1; }


        var t = new Date();
        for ( var i = reps; i >=0; i-- ){ f( i ); }
        t = ( new Date() ) - t;

        alert( 'Avg function time:' + ( t/reps ) );
    }
</script>
<button onclick="doTest()">Test</button>

Laszlo targeted the Flash runtime because it's fairly ubiquitous, and because it's a good and consistent software renderer. It also, of course, at least has a bytecode interpreter, which was a requirement for us, but it's not a very performant one -- and that has made it harder than it should be to both evolve the Laszlo platform, and to develop Laszlo apps.

Performance is always a huge consideration when developing client software, but it is especially so for rich internet apps, where the user's expectation has been set by quick rendering HTML pages. Often, the improved experience that an RIA offers ultimately makes a slight pause during startup worthwhile, but if users bail before the app starts up, then they'll never get a chance to know this.

I have spent a lot of time optimizing both the Laszlo runtime and Laszlo applications. The good news is that even the most awful seeming Laszlo app can usually be optimized and made to startup quickly and perform adequately. There are a number of strategies for this, and hopefully I will someday cover all of them here. For this entry, though, I have chosen to focus on Laszlo's initstage attribute, which offers the developer some choices around how portions of a Laszlo program are initialized.

This discussion does not include an overview of Laszlo's Krank feature, which is often an extremely simple and efficient way of optimizing a Laszlo app. Krank does, however, introduce some complications into the deployment path, and it also makes apps significantly bigger.

The basic strategy I want to demonstrate here is to defer the creation of views which aren't needed when a program starts. The example below contains a class which simulates a component that takes a long time to initialize. Note also the use of the <inittimer> tag, which provides an easy way to track app startup time.

For the purposes of demonstration, I've made it so you have to click the HTML button below to load the Laszlo app. This allows you to see a slow starting app without making this whole page sluggish.


<canvas height="200">
    <inittimer/>

    <class name="slowComponent" width="150" height="16" bgcolor="gray">
        <!-- time in ms to wait before initing -->
        <attribute name="extratime" value="500"/>
        <method name="init">
            <![CDATA[
            var d = new Date();
            while ( (new Date() ) - d < extratime ){};
            super.init();
            ]]>
        </method>
    </class>

    <button y="25" onclick="slowin.show()"> Show window
    </button>

    <window name="slowin" x="100" width="200" height="200" visible="false"
            title="slow window">

        <method name="show">
            this.setVisible( true );
        </method>

        <view name="content">
            <simplelayout spacing="2" />
            <slowComponent/> <slowComponent/>
            <slowComponent/> <slowComponent/>
            <slowComponent/> <slowComponent/>
        </view>
    </window>
</canvas>

As you can see, this app takes a little over three seconds to initialize, mostly because of the half-second delay caused by each instance of slowComponent.

Since we don't need the window when the app starts (let's say it's a preferences dialog or similar) we can defer the creation of the window contents by using Laszlo's initstage attribute.



<canvas height="200">
    ...

    <window name="slowin" x="100" width="200" height="200" visible="false"
            title="slow window">

        <method name="show">
            this.setVisible( true );
            content.completeInstantiation();
        </method>

        <view name="content" initstage="defer">
            <simplelayout spacing="2" />
            <slowComponent/> <slowComponent/>
            <slowComponent/> <slowComponent/>
            <slowComponent/> <slowComponent/>
        </view>
    </window>
</canvas>

The call to completeInstantiation in the window's show method ensures that the window contents get created when the window is shown. As an aside, you might imagine that the instantion of a declarative program (especially one which can include constraints where a refers to b and b refers to a) is somewhat complex. It certainly is, and I don't want to cover how a Laszlo program initializes here. However, the initstage tag simply governs how a node and its parent initialize.

"Defer" on a view means that the view's parent should not create that view until completeInstantiation is called. Since the semantics of 'init' are such that a view calls init when all of its subviews have inited, you will note that the content view in the example above is not inited until completeInstantiation is called. I encourage you to copy and edit this program to verify this for yourself. Finally, it's worth noting that it's fine to call completeInstantiation repeatedly.

So this fixes the startup time problem, but we're still left with an ugly pause when the user hits the button. There's another improvement we can make here, which is to show the window before all of the slowComponents have inited. Again, Laszlo provides a fairly simple construct to make this possible.



<canvas height="200">
    <inittimer/>

    <class name="slowComponent" initstage="late">
        <!-- time in ms to wait before initing -->
        <view width="150" height="16" bgcolor="gray">
            <attribute name="extratime" value="500"/>
            <method name="init">
                <![CDATA[
                var d = new Date();
                while ( (new Date() ) - d < extratime ){};
                Debug.write( 'init' );
                super.init();
                ]]>
            </method>
        </view>
    </class>

    <button y="25" onclick="slowin.show()"> Show window
    </button>

    <window name="slowin" x="100" width="200" height="200" visible="false"
            title="slow window">

        <method name="show">
            this.setVisible( true );
            content.completeInstantiation();
        </method>

        <view name="content" initstage="defer">
            <simplelayout spacing="2" />
            <slowComponent/> <slowComponent/>
            <slowComponent/> <slowComponent/>
            <slowComponent/> <slowComponent/>
        </view>
    </window>
</canvas>

Note that the structure of slowComponent had to change a little to make this possible. The reason for this change turns on the meaning of initstage="late". This can be confusing, and in fact, when I went to write this last example, I simply tried this:

<class name="slowComponent" width="150" height="16" bgcolor="gray"
    inistage="late">
    ...

But this has no effect. Why? The answer is that initstage="late" means: "init my children in the background, and allow my parent to init without me." However, completeInstantion means: "force my children to init right now." When completeInstantiation is called on the content view, it forces the initialization of all of the slowComponents. Since the slowComponents have no children, they simply apply their attributes and run their init routine, even though they are inistage="late".

With the change made in the last example above (where it's the child of slowComponent that takes a long time to init,) the content view instructs its children to initialze, but those children are themselves initstage="late", meaning that their children get put on a trickle queue, and their construction and initialization happens in small slices in the background, even while the program takes user-input and processes other interrupts. It should finally be noted that initstage="late" views can be forced to init completely by calling completeInstantiation on them, just like deferred views.

Fortunately, a real component that takes a long time to initialize is usually just a container for complex, slow-to-initialize contents. This means that it's often enough to set initstage="late" on classes or intstances of classes.

One last note about initstage="late": it may be tempting to simply use it on, say, the window that is not shown when the program starts. In practice this is usually a bad idea because the views that are getting contructed and initialized in the background slow down the flash player to a user-perceptible degree. It's almost always preferable to defer the creation of a container for views that aren't needed at startup-time, and then to use initstage="late" on those contents.

We used some of these techniques to speed up the start of Laszlo's homepage app. You'll recognize the behavior of the dropdown windows, where you can see the late initialization of the window contents when they are first dropped down.

The Working Class

I've been critical of classic, class-based OOP in this forum, so I decided it was time to point out a place where I think it works really well in Laszlo. I really don't like iterators in Java -- I think they're cumbersome, and it irritates me that I need a whole object just to represent what is essentially a counter. However, a layout in Laszlo is an iterator that works well as an object, because it has meaningful state.

A layout tends to control its siblings. Even novice Laszlo users are familiar with a construct like this:

<canvas>
    <class name="box" width="100" height="10"/>
    <box bgcolor="red"/>
    <box bgcolor="blue"/>
    <box bgcolor="green"/>
    <simplelayout name="myLayout" spacing="2"/>
</canvas>

(EDIT PROGRAM)

Here myLayout operates on the colored boxes that are its siblings. One thing that's nifty about layouts is that they can be active -- they can be written to respond to changes in the attributes that affect the layout. Simplelayout, for instance, knows that it is affected by changes to the size of a view in the layout axis. The layout in the example below responds as the boxes change height when they are clicked.

<canvas>
    <class name="box" width="100" height="10"
           onclick="this.animate( 'height' , 10 , 300 , true )"/>
    <box bgcolor="yellow"/>
    <box bgcolor="teal"/>
    <box bgcolor="maroon"/>
    <simplelayout name="myLayout" spacing="2"/>
</canvas>

(EDIT PROGRAM)

Simplelayout is aptly named: it has fairly simple behaviors, and if it didn't have a few important optimizations; it'd be less than 30 lines. A layout has several useful members that are provided by the base class:

  • An abstract update routine
  • This is the meat of the layout.

  • A subviews array
  • The list of views controlled by the layout.

  • A handy API
  • That makes it easy to reorder or change the subviews array.

So using the basic features of layout, I can reconstruct simplelayout in about 10 lines of code (and demonstrate my familiarity with the more obtuse CSS colors).

<canvas>
    <class name="box" width="100" height="10"/>
    <box name="box1" bgcolor="silver"/>
    <box name="box2" bgcolor="fuchsia"/>
    <box bgcolor="navy"/>

    <button onclick="with( parent ) aLayout.swapSubviewOrder( box1, box2 );">
        Swap
    </button>

    <layout name="aLayout">
        <attribute name="spacing" value="2"/>
        <method name="update">
            <![CDATA[
            var pos = 0;
            for ( var i = 0; i < subviews.length; i++ ){
                subviews[ i ].setY ( pos );
                pos += subviews[ i ].height + spacing;
            }
            ]]>
        </method>
    </layout>
</canvas>

(EDIT PROGRAM)

Note that all of the heavy lifting of registering added subviews and calling the update routine is done for me -- I just have to add the logic that knows what do when the layout updates.

From this, it should be clear that layouts do not have to be solely position related, and multiple layouts can apply to a given view. Here's a layout that sets the background colors of the views it controls:

<canvas>
    <class name="box" width="100" height="10"/>
    <box/> <box/> <box/> <box/> <box/> <box/> <box/> <box/>

    <simplelayout/>

    <layout>
        <attribute name="colorIncrement" value="0x202020"/>
        <method name="update">
            <![CDATA[
            var color = 0;
            for ( var i = 0; i < subviews.length; i++ ){
                subviews[ i ].setBGColor ( color );
                color += colorIncrement;
            }
            ]]>
        </method>
    </layout>
</canvas>

(EDIT PROGRAM)

Some people are surprised that layouts are themselves objects. This was, in fact, a design choice that was made during the development of language. Laszlo provides some syntactic sugar to allow developers to specify a layout as an attribute (<view layout="class: 'wrappinglayout'">) but heavy users of LZX understand that by making layouts share the API of the Laszlo base class (LzNode), and by giving them access to such features as constraints and events, layouts can be much more abstract and powerful.

For instance, here's a layout that extends simplelayout to make it fill its container with instances of a class that is passed to it as an attribute.

<canvas>
    <class name="box" width="100" height="10" bgcolor="red"/>

    <class name="fillLayout" extends="simplelayout">
        <attribute name="fillClass" when="once"/>

        <method name="update">
            <![CDATA[
            super.update();
            var lastsub = subviews[ subviews.length - 1 ];
            while( !lastsub || lastsub. y < immediateparent.height ) {
                var lastsub = new fillClass( immediateparent );
            }
            ]]>
        </method>
    </class>

    <window height="200" width="100" resizable="true">
        <fillLayout fillClass="box" spacing="5"/>
    </window>
</canvas>

(EDIT PROGRAM)

This is almost embarassingly easy to write, and you should experiment with editing the source to make the fillLayout create, say, buttons instead of boxes. If you play with this, though, you'll note that if you resize the window, the layout doesn't create new views. This is because the layout has not registered its dependency on the size of its immediate parent. Lucky for us, the layout base class comes with an updateDelegate, which is a callback that can be registered with events that, when sent, make the layout run its update routine. All we have to do is register this callback with the onheight event of the immediate parent, like so:

    ...
    <class name="fillLayout" extends="simplelayout">
        <method event="oninit">
            updateDelegate.register( immediateparent , 'onheight' );
        </method>
    ...

(EDIT PROGRAM)

and now the layout responds as the window resizes.

Hopefully, you're now convinced that layouts are fairly easy to write. Experience has also shown that layouts tend to be a useful optimization tool. Some relationships are just easier to express in a for loop than in a web of declarative code, and layout provides a useful bridge between the worlds of declarative and procedural coding.

Ultimately, I think the layout base class works well for a few reasons:

  1. it's abstract
  2. A layout does not try to model user-land; a layout tries to model developer-land. It makes sense for a tool used by a developer to have a constrained and well defined API; it doesn't make that much sense for a user. It's interesting that a relatively complex thing like fillLayout (above) is much shorter than the extensions made to the basebutton class by Laszlo's concrete button class. OOP is poorly suited to the idiosyncratic and somewhat whimsical demands of user-experience.

  3. it's a container
  4. I think OOP is best suited to containers. This is the essence of the C++ STL, and it makes it all the more ironic that the princess of OOP, Java, has (up 'till now at least) had such problems with genericity. No one wants to have to write a hash map or an indeterminite-length list over and over (although it seems like in most languages, we still do.)

  5. it's unambitious
  6. Essentially, a layout is just an array of items -- wrapped with some useful methods and hooked into the Laszlo runtime instance hierarchy in useful ways. Layouts don't try to proscribe what a developer can do with them, nor do they make any assumptions about what purpose they ultimately serve.

I wish I could say that classes work this well for more of the Laszlo components, but in general, I think class-based OOP is bad for UI programming.

State of the State

Oliver Steele has written about the way in which Laszlo's use of prototypes enables what he calls "instance-first development." I'm glad he did, because it lays the groundwork for what I wanted to write about in this entry: Laszlo's state tag, and its meaning within Laszlo's meta-object framework.


That may sound like a lot of fancy talk, but it's not that complicated, and it cuts to the heart of the declarative programming model. We made a simple observation early on in the development of LZX: declarative programming is great for declaring the initial state of an application, but subsequent state was relatively difficult to represent.


For a little while, our story was simple: declare the initial state of your program with tags, and then manipulate it with code. This was acceptable, but there was a problem.


We had built a powerful declarative language, but developers could only use its features to express initial program state. This was particularly true of animators and constraints. Constraints are especially powerful, and they are difficult to add programatically, as they tend to involve some tricky function-as-datatype business that can be unsavory. For instance, here's a small program where the blue box follows the mouse:

<canvas>
    <view bgcolor="blue" width="20" height="20">
        <attribute name="x" value="${parent.getMouse( 'x' ) - 10}"/>
        <attribute name="y" value="${parent.getMouse( 'y' ) - 10}"/>
    </view>
</canvas>


(EDIT PROGRAM)

While this is an especially nice way of expressing this kind thing (made possible by Laszlo's constraint system,) it's rarely useful in a program. Usually, objects only follow the mouse when they're being dragged. Clearly we needed a way of writing declarative LZX nodes that could be applied to and removed from program state.


We introduced the state tag to make this possible. Here's a rewrite of the above program using states, to make the blue box drag only when the mouse is down:

<canvas>
    <view bgcolor="blue" width="20" height="20"
          onmousedown="this.setAttribute( 'mouseIsDown' , true )"
          onmouseup="  this.setAttribute( 'mouseIsDown' , false )">
        <attribute name="mouseIsDown" value="false"/>
        <state apply="${parent.mouseIsDown}">
            <attribute name="x" value="${parent.getMouse( 'x' ) - 10}"/>
            <attribute name="y" value="${parent.getMouse( 'y' ) - 10}"/>
        </state>
    </view>
</canvas>


(EDIT PROGRAM)

Note that the syntax of the constraints hasn't changed; they're just inside a state tag. This is useful, but it's also a little strange: the state tag is essentially an invisible node of hierarchy. You can see this in the fact that the parent in the apply constraint for the state is not the same object as the parent in the drag constraints inside the state.


This example highlights the metaphysical nature of states -- they modify the meta-object protocol enough so that their contents can be stored and applied elsewhere. This could conceivably be a feature of the system as a whole (there's a feature request in our bug DB for anLzNode.setParent( otherLzNode ),) but it's not.


Anyhoo, here's the interesting bit: what should happen when I write this?

    <class name="myDragState" extends="state">
        <attribute name="dragMin" value="2"/>
        <attribute name="dragMax" value="20"/>
        ...
    </class>

The question is: do the attributes dragMin and dragMax pertain to the state itself, or to the state's container? The former is more powerful: it would allow for developers to extend the special behavior of states, say by over-riding the state's apply method. The latter, though, is more consitent with the lzx idiom, where, as a first-order approximation, a class behaves like a macro.


For now, the behavior is the latter. Here's a little test program that shows that.

<canvas>
    <class name="constrainedDragState" extends="state">
        <attribute name="dragMin" value="100"/>
        <attribute name="dragMax" value="300"/>
        <attribute name="mousePos" value="${parent.getMouse( 'x' ) - 10}"/>
        <attribute name="x"
                   value="${Math.max( Math.min( this.dragMax , this.mousePos),
                                      this.dragMin )}"/>
    </class>

    <view bgcolor="blue" width="20" height="20" x="100" y="50"
          onmousedown="myDrag.apply()"
          onmouseup="  myDrag.remove()">
        <attribute name="mouseIsDown" value="false"/>
        <constrainedDragState name="myDrag"/>
    </view>
</canvas>


(EDIT PROGRAM)



Note that the references to dragMin, dragMax and such are all made within "this."


One way this could be clarified would be to make the somewhat arbitrary, but sensible, distinction that any attribute for which the state has a setter belongs to the state itself, whereas any attribute that doesn't belongs to the state's apply target. This makes sense, because any setter for an attribute which is intended to be applied by the state (and not kept by the state) can be written in the apply target. By something of a coincidence, this is how it currently works in Laszlo. Here's a little program that proves that:

<canvas debug="true">
    <class name="testState" extends="state">
        <attribute name="countApplies"  value="0"
                   setter="this.countApplies = countApplies"/>
        <method name="apply">
            this.setAttribute( 'countApplies', ++this.countApplies );
            super.apply();
        </method>
    </class>
    <button >Try it
        <method event="onclick">
            ts.apply();
            Debug.write( "applies:" + ts.countApplies) ;
            ts.remove();
        </method>
        <testState name="ts"/>
    </button>
</canvas>


(EDIT PROGRAM)



One last thing: developers are often surprised to find that one pays the instantiation cost of a regular node of hierarchy for a state, whether or not it is applied. This is because the state is itself an LzNode, which must be processed by the LzNode constructor. The advantage of limiting the ways in which a developer can modify a state itself is that then a given state can have a limited number of attributes, which are controlled by the runtime. This would allow for an optimization: every instance of a class could share a single instance of each state it contains. Allowing developers to muck with the semantics of "state" by attaching attributes and methods to it would make it harder to make this optimization.

It's the classic story: features or performance?

Programs as Data

I love runtimes. Leveraging increasing processor speed to create useful abstractions for the programmer is an idea which (I think) is just in its infancy, but which will ultimately become the basic model of software construction.

The runtimes for Java and C# have at their core a single feature: a memory manager. That alone has proven so worthwhile that it has created a tectonic shift in modern software development. It's interesting (and a little sad) to me that this is really the only abstraction built into these runtimes, as I think that loose (or at least non-overt) typing, combined with more flexible data types (I want to be able to add numbers to strings, for example) make for shorter, easier to read, and less buggy programs.

This is my bias, and I don't want to argue it right now. The thing that really interests me about runtime technologies is that if the runtime designer can identify a few core, powerful constructs and then build those constructs into the runtime in native code, then she can create a runtime which will have decent performance characteristics even though it offers very high level constructs.

Something I've been thinking a lot about lately is the way in which a program which is run by a runtime is actually just data. This is somewhat obvious, but it is rarely formalized. In fact, runtime languages usually do a lot to look like the compiled languages that preceeded them. The fact that I have to think about whether I want an int or a float in Java strikes me as ridiculous. If I don't care, I shouldn't have to think about it.

This is a side note, but I also think it's interesting that performance issues often keep real programs from taking full advantage of Java's built-in garbage collector. In theory, it's great that everything in Java is and should be an object; in practice, programmers often have to write their own object pools to workaround the slowness of allocating memory. This is another way in which the full power of the Java runtime cannot be exploited.

Anyway, back to the main point, which is that to date, the virtual machine abstraction has been relatively weak: the Java and MS.NET runtimes are essentially idealized versions of modern computer architecture that don't leak memory or crash (at least, they're not supposed to leak memory or crash.) This reminds of me of something I heard about the early days of cinema: there was a while, before Eisenstein and others invented a cinematic vernacular, where people made movies by just locking down a camera and filming a play.

Functional programming is the closest match to the kind of abstractions that I'm talking about, but I find that purely functional languages are cumbersome for all but the most trivial applications. XSLT is the functional language that's probably most widely used today, and I think that anyone who has used it will testify to its deficiencies. It's good at simple things, but the exceptional cases, the idiosyncracies in the data model, and the inevitable mid-project changes, tend to be very difficult to deal with using XSLT. More importantly, the missing if...then and for...in type of logic that is honestly the bread and butter of most production code is hard to recreate in functional languages, and the implementation tends to be fragile, so that making a change to logic forces large rewrites to the program.

Laszlo currently has an interesting mix of functional and imperative programming. I've written about Laszlo's use of XPath before, but here's how it relates to program transformation.

(EDIT PROGRAM)

<canvas layout="spacing: 2">
    <dataset name="myData">
        <people>
            <Tim> Nice guy.  </Tim>
            <Ron> Not so bright.  </Ron>
            <Kelly> Tall.  </Kelly>
            <Adam> Bombastic.  </Adam>
        </people>
    </dataset>

    <view datapath="myData:/people/*" layout="axis:'x'">
        <text datapath="name()"/>
        <text datapath="text()"/>
    </view>

</canvas>

Here at Laszlo, we call this effect "data replication." This program essentially says: for each of the nodes in myData:/people, make a view which has two children, which are text's, the first of which has the text of the node's name(), etc.
The nice thing about Laszlo is that I can do this:

(EDIT PROGRAM)

<canvas layout="spacing: 2">
    <dataset name="myData" src="mydata.xml"/>

    <view fontstyle="bold" layout="axis:'x'" name="title">
        <text>Name</text>
        <text>Comment</text>
    </view>

    <view datapath="myData:/people/*" layout="axis:'x'" name="people">
        <text datapath="name()"/>
        <text datapath="text()"/>
    </view>

</canvas>

This is a simple exception to the main program logic -- I want column headers in a slightly different presentation style -- but I can add them to my program without having to add them to my data model and then try to identify that data in my for..each transformation. Again, if you've used XSLT, you know how hard it can be to add these types of things at just right spot in the middle of a complex transformation.

I'm pretty happy with the way that data replication works with Laszlo, but it's a little bit ad-hoc. The transformation of the people view happens at runtime, meaning that in order to know whether or not there are one or many people view's, the first people view has to be made. In practice, this makes for some messy programming, because the first people view is subtly different from the others. This can be especially frustrating, because often the programmer knows at design time whether or not she intends a given data-mapped view to replicate.

What we've been talking about lately is making it so that the runtime is essentially a big data replication engine. In Laszlo as it is implemented now, there isn't really an abstraction layer between the part of the above program that specifies the title view, and the runtime objects which are created to represent it. The transformation from the declaration of that view to a constructor call happens in the compiler. What I've been thinking about is the idea of passing a Laszlo program to the runtime as data. The Laszlo language, then, is a default mapping that tells the runtime how to map data to constructor calls. The default mapping is:

XML node Laszlo instance
name class
attributes attributes
children children (recursive)

A datapath declaration is way of using other data as program data, and changing that mapping. In the above program, the replicated row does this:

XML node (of myData:/people) Laszlo instance
"view" class
"people" name attribute
"axis:'x'" layout attribute
text (with separate mapping) first child
text (with separate mapping) second child
name ignore
attributes ignore
children ignore

I think it would be so nice if this was built into the core of the Laszlo runtime. (Instead, right now, the people view is responsible for figuring out whether it needs to replicate. The program really is a program, and the data is fed into it.)

When we've talked about this at Laszlo, people's reaction has been either "that's too abstract for me to understand how it's useful" or "it sounds right, although I don't know specifically what it would buy us."

I think that the main advantage is that this would remove the distinction bewteen data-backed parts of program and non-data backed ones. Any part of a Laszlo program hierarchy could be read back out as data, and more importantly, component writers would not have to write special code for the data-backed case.I think it would eliminate a whole class of bugs, and it would open the door to a much more formal description of Laszlo programs, which might lead to compile-time optimizations. Besides, it's just really cool.

|
* * *
Syndicate content