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?

|
* * *