Infrequently Noted

Alex Russell on browsers, standards, and the process of progress.

delegate(), delegate(), delegate()

My MBP batteries keep dying after about a year (each). I usually have 2 that I tote around with me, and each tends to be good for 1.5-2hrs of actual work. This means that I tend not to be able to work through a cross-country flight, and particularly not if I need a VM for anything (which is most of the time). I think that if Apple does rev the MBP's on the 14th, the things I'd pay for boil down to "more memory and much longer battery life". The 5+ hour flight to TAE then provided a short window to do work in before I retreated to watching episodes of The Colbert Report on my phone. Knowing that i wouldn't be able to work the whole time, I brought a copy of a great paper on Traits. The paper got me thinking a lot about dojo.declare() and dojo.delegate().

Today, Dojo's delegate() function is a straightforward implementation of the Boodman/Crockford delegation pattern which Doug calls "beget" and which ES 3.1 will refer to as Object.create:

dojo.delegate = (function(){
    // boodman/crockford delegation w/ cornford optimization
    function TMP(){};
    return function(obj, props){
        TMP.prototype = obj;
        var tmp = new TMP();
        if(props){
            dojo._mixin(tmp, props);
        }
        return tmp; // Object
    }
})();

This function returns a new object which looks to the old object for things it does not itself have. Imagine an object foo which contains pithy truisms:

var foo = {
  science: "rocks!",
  learning: "is how you know you're alive"
};

We now want to promigulate our opinions, so we can delegate the responsibility of forming them:

var bar = dojo.delegate(foo, {
  testify: function(){
    console.debug("science ", this.science, "and learning", this.learning);
  }
);

Now, our bar object can change its mind independently of foo, but until it does, it'll behave as though foo's views are its own:

bar.testify(); // outputs: "science rocks and learning is how you know you're alive"

// bar refines its opinion bar.science = "is a process"; bar.learning = "requires humility"; foo.science == "rocks!"; // still true

bar.testify(); // outputs: "science is a process and learning requires humility"

But what about when the chain gets deeper? The fact that bar can't "see" foo's values via this isn't much of a problem when the hierarchy isn't very long, but if you're specializing a behavior or complex interaction, making it possible to get at the parent's values for properties and methods becomes more pressing.

Neil has previously written about lightweight subclassing, but for as good as it its, it doesn't get us all the way there either. In regular OO-style languages, the inheritance system gives you an out via a "super" keyword or convention. This type of property shadowing-with-exceptions is a huge boon to composition in class-based languages, but it's not the whole story. Indeed, the Traits paper was all about the shortcomings of this special-purpose mechanism. What we want for both long delegation chains and long inheritance hierarchies is a more general system; in essence a way to say "I want to control how things are shadowed and which ones an item points at in each level of the hierarchy".

What if we could make delegate() savvy of this type of indirection? Here's my quick prototype:

delegate = (function(){
    var tobj = {};
    var TMP = function(){};
    return function(obj, props){
        TMP.prototype = obj;
        var tmp = new TMP();
        if(props){
            var remaps = props["->"];
            if(remaps){
                delete props["->"];
                for(var x in remaps){
                    if(tobj[x] === undefined || tobj[x] != remaps[x]){
                        if(remaps[x] == null){
                            // support hiding via null assignment
                            tmp[x] = null;
                        }else{
                            // alias the local version away
                            tmp[remaps[x]] = obj[x];
                        }
                    }
                }
            }
            dojo.mixin(tmp, props);
        }
        return tmp; // Object
    }
})();

This new version of delegate() accepts a specially named "->" property in the list of items to add to the destination object. Items in this list can either "shadow null" (hide entirely) the parent's property or can provide a new name for it, assuming of course that the new object will also have a property of that name. Here's a quick example of "->" at work with our previous example. This time, foo also has a "testify" method that we'd like bar to be able to control without having to copy the implementation:

var foo = {
    science: "rocks!",
    learning: "is how you know you're alive",
    testify: function(){
        console.debug("science ", this.science, "and learning", this.learning);
    }
};

var bar = delegate(foo, { "->": { "testify": "grampsSays" // maps foo's "testify" to bar's "grampsSays" }, testify: function(){ if(this.science && this.learning){ this.grampsSays(); // call the re-named "testify" }else{ console.debug("this object is strikingly ignorant"); } }, });

bar.testify(); // outputs: "science rocks and learning is how you know you're alive" bar.science = false; bar.testify(); // outputs: "this object is strikingly ignorant"

That New Object Smell

The last missing piece of the hierarchy pie here is that there's no initializer for the objects which come from a delegation. A simple addition of some property detection code to look for an initializer can easily handle that:

delegate = (function(){
    var tobj = {};
    var TMP = function(){};
    return function(obj, props){
        // boodman/crockford delegation w/ cornford optimization.
    TMP.prototype = obj;
    var tmp = new TMP();
    if(props){
        var remaps = props["->"];
        if(remaps){
            delete props["->"];
            // like dojo.mixin(), except w/o key/key mapping
            for(var x in remaps){
                // "safe" copy properties
                if(tobj[x] === undefined || tobj[x] != remaps[x]){
                    if(remaps[x] == null){
                        // support hiding via null assignment
                        tmp[x] = null;
                    }else{
                        // alias the local version away
                        tmp[remaps[x]] = obj[x];
                    }
                }
            }
        }
        dojo.mixin(tmp, props);
    }

    // support for "constructor" functions. The name "init" is arbitrary.
    if(typeof tmp["init"] == "function"){
        tmp.init.call(tmp);
    }

    return tmp; // Object
}

})();

And there we have it. A style of delegation that easily supports both Trait-like name aliasing (and null shadowing) as well as internal initializers. Since our upgraded delegate can handle nulling out a parent's value for a property, we also have a straightforward way to prevent parent initializers from being called (or being called/chained - at our discretion - by a new name):

var foo = {
    science: "rocks!",
    learning: "is how you know you're alive",
    testify: function(){
        console.debug("science ", this.science, "and learning", this.learning);
    }
};

var bar = delegate(foo, { init: function(){ this.testify(); } }); // outputs: "science rocks and learning is how you know you're alive"

var baz = delegate(bar, { // map away the parent's constructor "->": { "init": "superInit" }, // provide our own constructor init: function(){ console.debug("howdy!"); this.superInit(); // call the super-object ctor } }); // outputs: "howdy", "science rocks and learning is how you know you're alive"

var thud = delegate(baz, { "->": { "init": null } // hide the parent ctor }); // outputs: nothing

This form of delegate is likely to appear in Dojo 1.3 along with similar improvements to dojo.declare() to help alleviate the composition problems associated with using complex sets of mixins.

Update: corrected the null-out branch and updated the text with Doug's note that beget/delegate will be called Object.create() in 3.1.

"Action Oriented Programming"

It's good to be back in SF after a pretty hectic week in Boston for Dojo Developer Days and The Ajax Experience. There's a lot to say about them, which hopefully I'll get to in a longer post. Our first DDD event under Pete's excellent leadership was a success and Dojo and SitePen very well represented at the conference.

While in Boston, Gavin and Jill joined a gaggle of Dojo hackers at a dinner ostensibly to mourn my birthday (thanks to Dylan and Pete for organizing!) and in the course of conversation Jill asked something along the lines of:

So why do people get so excited about closures?

Which prompted a several of us to flail and flop gasping the salt flats of analogy like fish out of polite-conversation water. After about 10 minutes of this, Jill succinctly summed it all up in the form of a question:

Oh, so it's like "action-oriented programming"?

This is perhaps the most insightful and succinct description I have ever heard of what JavaScript is all about.

Update: Jennifer just played this for me and it gets right to the heart of this post: the important part of doing what we do with computers, and more importantly, with the web, is to give the power of Computer Science to real people...and it starts with insights like Jill's that build a shared way of thinking and talking about the world. It makes me sad that many programmers miss that, but when non-programmers can share in the beauty and power of code, it does a lot to make it all seem worthwhile.

ZendCon Notes

I gave a talk on Dojo Wednesday at ZendCon, and when I walked into the room for the talk, there was some disorder as the conference center staff were taking out the tables to fit more chairs in. Even with the extra space, the room was totally packed, thanks in large part to the amazing Dojo integration work that the Zend team has done.

As of Zend Framework 1.6, you can include some trivial code inside your ZF views to pull in Dojo:

< ?
	// setup required dojo elements:
	$this->dojo()
		->enable();
echo $this->dojo();

?>

Once enabled on your page, ZF 1.6 also includes a full set of helpers to let you set up Dijit components from PHP. The excellent ZF docs has the full story. Perhaps most exciting from my perspective, though, is how simple ZF makes getting up-and-running with Dojo and how nicely it ties in with custom builds and CDN-hosted versions of Dojo as well. Matthew Weier O'Phinney and Will Sinclair recently did a screencast that walks through a lot of these options. If you're considering ZF+Dojo, I strongly recommend you check it out.

The talk I gave on Wed was mostly focused on Dojo and the reasons we built it in the layered way that we have and how you can choose to use Dojo at whatever level of abstraction feels right for your app. Slides are here (5.1MB, PDF):

Matthew Russell Keeps The Good Stuff Coming

Matthew Russell, author of ORA's "Dojo: The Definitive Guide" now has a companion blog where he's posting new widgets complete with screencasts to explain them clearly. His awesome first outing includes a neat reflection widget that builds on AOL's high-performance CDN hosting of Dojo and practices what Dojo preaches about pragmatic progressive enhancement. Awesome stuff!

By The Numbers

Note: contents of this post is after the jump due to its political nature.

I was just noting to Jennifer yesterday a couple of days ago that my biggest frustration with the debate about Palin's transparent misrepresentations about earmarks is that they shouldn't even be the issue. The big lie here is that earmarks, for as vile as they may be, aren't going to actually make a difference to the country's fiscal situation. As I returned from a client site on Friday and fired up my RSS reader for the first time in a week, I found that (as usual) Mark Thoma is already on the case, aptly pointing out in a single post that McCain's "economic policy" prescriptions to date amount to little more than misdirection in the face of what can only be described as a foreclosure crisis, a market melt-down, a flagging dollar which threatens our unique position as the world's reserve currency, huge inflationary pressure due to moribund energy policy, and yawning fiscal deficits (which, yes, have a lot to do with an ill-advised war and deeply regressive tax cutting). The over-compensating assessment by Thoma is that 0.58% of the budget is directed toward earmarks. Will someone please wake me when McCain starts talking about spending priorities which might actually matter?

To see if the McCain campagin was just making hay over a perceived weakness of the opponent, I checked out John McCain's website for his economic "policy". You can't get very far in most of it before noting that it's all about oil and drilling. To wit, even when describing the need to "eliminate wasteful government programs" the McCain policy statement doesn't name a single program to be trimmed. Looking for a detailed set of policy priorities on the McCain site is a dead-end. The oft-repeated claim that a McCain administration's encouragement of drilling as a way to "send a signal to the market" regarding the price of oil is, by all industry accounts, laughable. As the graph (via Mark Thoma, via Grist) makes clear, drilling ain't gonna get us there either:

Claims regarding the encouragement of nuclear power generation capacity completely gloss over the complexities of permitting, time-to-build, and financing these projects, not to mention the as-yet un-finished business of long-term waste disposal, a topic that McCain himself seems to be of two minds about. Solar and other renewables actually have the property of being feasible on a much smaller scale, allowing them to be implemented gradually. For all intents and purposes, even if nuclear were cheaper per KW/hr (it's not), it would still be a risky capital investment (likely borrowed) for any utility – therefore the cost of servicing debt becomes a major factor in the ability for nuclear to take off. Why would any utility exec take the risk on a 5 to 10 yr project with a high likelihood of failure with many of the dominant costs of both construction and operation completely up in the air when you can just additively expand your capacity with solar or wind with lower up-front costs, no waste-disposal risk, and the potential to start producing in the 1-2 yr timeframe? And given this reality, why doesn't the McCain energy policy acknowledge that these alternatives need the same level of government support as drilling and nuclear? And why would McCain not do the easy thing and support the existing (paltry) support that the government offers to these incremental, renewable energy sources?

In reading the McCain policy statments, I'm left with a long list of things that a McCain administration will somehow accomplish without any credible details regarding how these goals will be met. The message here is that John McCain, through completely un-specified means, will meet all of the lofty goals outlined in his policy wish-lists (which, given the lack of detail, is what they amount to). Perhaps we're supposed to believe that his personal character and skill as a politician will somehow overcome the yawning credibility gulf of his statements and the reality that he'll be dealing with a Democratically controlled congress should he win. No politician should, in the heat of a campaign, box themselves in too far by promising specific agendas which they may not be able to deliver on, but the lack of coherent supporting details by the McCain campaign goes directly to the issues of candor and credibility. While I would have voted for him in 2000, there's no way that I can justify supporting a candidate now who is willing to push fairy tales about the economic realities and forces which drive our economy. Seriously, the McCain website still includes the much-derided "gas tax holiday" proposal. When high-school economics is enough to destroy the credibility of your policies, then you're either pandering (a form of lying) or are simply ignorant. What we've got here is not a failure to communicate by the McCain campaign, it's a failure to face reality head-on. It's voodoo economics all over again.

It's no wonder that the McCain campaign is trying to make this election about something other than issues...the senator's written-by-lobbyists policies just won't stand up to anything like the level of scrutiny that the Obama plans will. Is the Obama plan for the economy a slam dunk? No, but it's a damn sight better than McCain's fairy tales.

Older Posts

Newer Posts