Infrequently Noted

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

dojofoundation.org Is Up!

My warmest thanks and a hearty "congrats!" to the Uxebu crew and Dylan for getting the new dojofoundation.org site up and running:

The new site is important for a couple of reasons. First, it's a good step in the direction of working to make clear that Dojo-the-javascript-toolkit and Dojo-the-foundation are completely separate entities with different goals and different leadership. The Foundation will take any and all projects that want a good home and are willing to meet the basic criteria that let others trust the code and process behind Foundation projects. It's been weird that most of the documentation about Foundation policies and procedures has lived on the Toolkit site for so long, and having them be totally separate should help clarify.

Secondly, the Foundation site helps show off the projects which aren't the toolkit. The Foundation has more than doubled in size in the last year (in terms of projects), and looks set to continue that expansion. That the Toolkit project gets the most attention has been a long-term irritant to nearly everyone involved, and it's my hope that the Foundation site will help to fix that.

Lastly, the Foundation site is beautiful. Built with the Toolkit and Dojango, the site is a great showcase for the kinds of things that are easy to build with Foundation-sponsored technologies. Nice work everyone!

Why Are We Even Having This Discussion?

Content after the jump due to the public policy nature of the post.

Here we go again.

Every four years we go into the partisan spasms of a heat-not-light [dis]enfranchisement "debate". What's perpetually lost is any coherent discussion of the causes of the debate. As with gerrymandering, the causes are structural and neither party feels it would serve them to effectively deal with the situation at it's roots. Not because solving the problem wouldn't be better for everyone (in both cases, it objectively would), but because it would change the game, and our 2-party system is nothing if not over-specialized to the current rules of the game. In this sense, both redistricting reform (killing gerrymandering dead) and automatic voter registration systems cause the old rules to lose their hold, instantly changing the calculus of holding on to power. Automatic registration via so-called "motor-voter" laws, registration with passport applications, automatic updates with change-of-address on US mail, and other sensible policies are opposed by partisans of both parties because they have the potential to allow discontent to cause large swings in electorate behavior. Today, between the systematic disenfranchisement caused by requiring citizens to register to vote and the effective gerrymandering-based dampening of popular will, enormous changes in likely voter feelings about the direction of the country (or, less often, a locality/district) are required to break an incumbent's grip on power. As an example, the "right-track/wrong-track" polling numbers in 2006 said that nearly 70% of Americans thought the country was on the wrong track. That feeling swept the Democrats into a razor-thin congressional majority (which the Republicans have been filibustering to death ever since), but in a 2:1 year, wouldn't you expect a similar landslide in Congress? Perhaps not in the Senate where fewer seats are up for a vote as a structural hedge against change, but certainly we should have collectively turned enough of the bums out to make the remaining ones fear for their jobs. As it happened, in a race with 33 seats up for discussion (15 held by Republicans), only 5 of the bums lost their seats. Now compare that to the most recent pre-Bush period of comparable "right-track/wrong-track" numbers: the Republican revolution" of '94. On that basis it becomes evident that a shift in power half-again as large – based on the same "market fundamentals" – was at least justified. 8 seats changed hands in '94 vs. 5 in '06. The difference? A census and therefore a chance for many states to gerry....er...redistrict. The calcifying power of...um...power worked its magic 2 years ago and the Senate is deadlocked as a result.

All of these effects cause fewer parts of the country to be competitive. If you support competition in markets (as I do), then there's no higher calling than supporting a competitive marketplace for ideas, and both parties are doing their best to stifle the pricing function of our public policy market. No wonder then that Republicans think they can steal (or at least dispute) an election by challenging voters in Ohio, despite many discredited attempts in the past to find large-scale vote fraud. Eight years of intense Republican scrutiny into the non-issue has utterly failed to turn up any massive conspiracy or even credible small-scale fraud. Remember, pressuring US AG's to prosecute thin or non-existent voter fraud cases – then firing them when they couldn't find any and/or wouldn't make them up – is what got Alberto Gonzalez hauled before congress for months on end, eventually leading to his disgraced resignation and continuing questions as to the impartiality of the DOJ. This isn't a winner of an issue for the GOP, but it sure is an effective way to try to keep the under-informed from coming to the polls and having their votes counted. And they know it. Scare-tactics and fear-mongering as [keep|get]-out-the-vote strategy. Democrats, naturally, feel that their on-the-ground vote registration in targeted areas can be an effective weapon in distorting the outcome.

Either way, it's shameful.

The Democratic approach is perhaps less reprehensible since the folks who are registered can vote however they please, but there's no denying the attempt at demographic distortion. But why should citizens need to register as a separate process from establishing residency, getting mail, or a driver's license anyway? And why isn't same-day registration the law of the land? If Wyoming can do it, why not New York and California?

Fundamentally, all the objections to broad enfranchisement end in a large-scale IT problem...but not a particularly hard one. What I'm not hearing from nearly anyone is a recognition that this shouldn't be a debate that happens in a functioning democracy. That we have it on a quadrennial basis is simply an embarrassing acknowledgment that our democracy is not functioning.

If McCain really wants to put "country first" and be a maverick, he'd be bringing this up as an urgent topic of national debate. It would certainly be better than the sleazy robo-call effort his campaign is now investing in. And if Obama wanted to build real credibility with Americans about his willingness to effect real change, shouldn't he start here? Certainly it's a better use of the national discourse than helping McCain draw unwarranted (and likely un-wanted) attention to a plumber. And where the hell is the 4th Estate when we need them?

Oh, right. Camped out on some plumber's lawn.

Luckily, I happen to live in California where the Governor (who I disagree with more than not) has put redistricting reform on the ballot in the form of Prop. 11. I'll be voting "yes" on 11, if only to make it that less likely that 4 years from now we'll have to have this "discussion" again.

Greg On Licensing

Greg Wilkins hits the nail squarely on the head:

At Webtide, we sell developer advice, custom development and production support for jetty and dojo cometd. We don't expect our clients to buy our services because of some sort of guilt trip from the value they obtain from those projects. We expect our clients to pay for the value add that we give. The software is free under the terms of the apache 2.0 license and we expect no charity or moral obligation in return.

This pretty much sum's up why most venture-backed Open Source efforts either fail so miserably at building real community or just fail miserably in general. Open Source – to my mind – isn't some talisman you wave over software to instantly take market share from entrenched players or to instantiate your very own +5 Army of Contributors (with the zealotry bonus) for personal gain. Instead, it's a great way to distribute software that should already be a commodity at near the cost of reproduction (roughly bupkis) and prevent network effects from ingraining outsized profits to firms whose marginal utility is suspect. If something is still worth paying for, it's natural to expect that it won't fare well in the world of free software. Too few people are liable to understand its value to create the virtuous cycle of contribution and use that makes the whole thing work. The great news here is that commercial software isn't dead at all. It just has to actually be better.

Open Source (and to similar and complementary extent, open standards) helps drive Pareto-efficient allocation of capital in the software business. That may not be a high calling, but it's a lot easier to justify than living off of monopoly rents for a living, and I take great comfort in knowing that what we do at SitePen adds amazing value (else we wouldn't make a living at it). As with Greg and WebTide, people pay us for what's actually scarce: clue, skill, and hard work.

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.