Constraint maintenance in Open Laszlo

Sometimes someone says something so clear that part of my worldview crystallizes, creating order where before there were only vague ideas. A few weeks ago, "tomcat is a servlet container" hit me this way. Today, it's getters and setters in Open Laszlo. In recent email to the laszlo-dev mailing list, P. T. Withington wrote:Before my time, I think we had getter's that pulled values, now we have setters that push them, and getters are only there for back-compatibility.

I understand this, I think: Open Laszlo is permissive about accessing an object's attributes. Open Laszlo maintains a one-way constraint propagation network. If value x changes, then any constraints which depend on x must also change.

<attribute name="x" value="70"/>
<attribute name="hotness" value="${x * 3}" />

To maintain the desired constraints, hotness must be recalculated whenever x changes. There are (generally speaking) two ways to maintain this dependency: "push" or "pull."

In a pull system, hotness would only be recalculated when it's accessed. Between the moment when x changes and the moment that hotness is accessed, hotness is in limbo; it doesn't reflect the value of x. That doesn't matter as long as you only access hotness with an accessor method. While the value of hotness is unused, it doesn't matter. However, if you change x and then access hotness directly, you are not going to trigger the constraint recalculation, and you'll get an old dirty value of hotness:

// Pull system example
Debug.print("hotness is ", myobj.hotness); // prints "hotness is 210
myobj.x = 12;
Debug.print("hotness is ", myobj.hotness); // error, prints "hotness is 210"

To get the right value of hotness in a pull system, you'd have to call the getter:

// Pull system example continued 
Debug.print("hotness is ", myobj.hotness); // prints "hotness is 210"
myobj.x = 12;
var t = myobj.getAttribute("hotness");
Debug.print("hotness is ", t ); // correct, prints "hotness is 36"
In a push system, hotness would be recalculated when x changes. The runtime maintains a list of the attributes that depend on x, and when x is changed, it tries to inform interested parties of that change. (This is really handled with delegates, but that's an implementation detail.) The runtime needs a chance to push the changed value into hotness and charm. It only gets that chance if a setter is called:
// Push system example
Debug.print("hotness is ", myobj.hotness); // prints "hotness is 210"
myobj.setAttribute("x", 12);
// Behind the scenes, delegates update hotness and charm.
Debug.print("hotness is ", myobj.hotness ); // correct, prints "hotness is 36"

Open Laszlo uses a push sytem for constraints. It is safe to get the value of an attribute with simple object access myobj.hotness but it is not safe to set the value of an attribute by direct access without calling the setter. To set an attribute which is an independent variable in constraints, you must call the setter." Setting a value directly, without the setter, can only be done if you don't need dependent constrained values to update. This is dangerous, but fast.

Understanding Tucker's quick quote explains why some code uses getters (an old idiom, no longer necessary, but valid) and some code uses setters. (They're required for constraint updates.) The correct, safe idiom is to only set attributes with setters. When this is too slow, setters can judiciously be replaced with direct assignment. Why do we use a push system? Performance. Typically the ratio of sets to gets of properties in a program is 1:4 1:5, so it is more efficient to only have to intercept the sets.

Postscript: Tucker adds, "setAttribute should be an implementation detail too, some day. The compiler really ought to intercept your attempts to set properties that constraints depend on and do the right thing."