MacPython Logo from __future__ import *

Kailash and Friends Kailash Kher Kaipa

online mp3 Anoice albums buy Amund Maarud albums online Asia online CD Andy M. Stewart buy tracks Axis online Astral Rising A Beautiful Machine download CD Aereda buy tracks Aksent online tracks Absidia Atrium Carceri A Beautiful Machine Absolum buy CD Aryan Wind and Brumalis and Valhalla Saints online music Atomsmasher download albums AK1200 download music Angelzoom online CD Arturo Mantovani and his Orchestra buy music 16 buy tracks Ashtorath online CD Aimee Mann buy music Anael And Bradfield buy mp3 Autumnblaze download mp3 Aggrolites download CD Arj Snoek buy albums Ada buy CD Aalto Andy With Rama West A Beautiful Machine Absolum online tracks Asura albums online Albert Lee 4 Non Blondes A Beautiful Machine Absolum download albums Andrew Lloyd Webber and Ar Rahman online music African Head Charge download mp3 Amber Asylum online music Analena online music ANTIX feat ROB SALMON A.R. Rahman A Beautiful Machine Absolum online tracks African Blackwood buy mp3 Axis buy mp3 Alan Menken buy music Amoebic Dysentery buy Alph Secakuku A Beautiful Machine albums download Albita online Amparo Ochoa A Beautiful Machine download tracks Andy Partridge and Harold Budd download tracks Anubian Lights Alient Project A Beautiful Machine Absolum buy albums Antonio Forcione download CD Ali G Indahouse online mp3 Art and Jazz Messengers Blakey download Arab Strap A Beautiful Machine online albums Adema buy Agua de Annique A Beautiful Machine buy CD Avalanches download tracks Acroma Andi Deris A Beautiful Machine Absolum download tracks American Steel download albums Amanda Perez online 999 A Beautiful Machine download mp3 Arild Andersen download CD American Steel buy tracks Absolute Beginner download tracks Anubi online albums Ancient Wisdom online A Verse Unsung A Beautiful Machine buy music Aghast Andromeda Island A Beautiful Machine Absolum download Arlo Guthrie A Beautiful Machine online mp3 Aavepyora online albums Achillea buy Andrew Bird A Beautiful Machine buy music Alexey Aigui and Ensemble 4'33'' albums buy Abbey Lincoln and Archie Shepp download albums Archive download CD A Guy Called Gerald feat. D.S. download music Al Di Meola online music Abigail download music Angel Witch online music Adelaide

2005-07-04

Comparison and Adaptation in JavaScript

Filed under: AJAX, MochiKit, javascript — bob @ 8:27 pm

JavaScript's comparators are flat out broken. They're marginally useful for value types, but without operator overloading or a sensible default, they're worthless for anything other than checking object identity.

In a language without useful operators, you effectively have to pretend that they aren't there anymore and just do everything with functions. MochiKit is a framework full of such functions that make JavaScript programming bearable. One of the most useful functions in MochiKit is the compare(a, b) function, which will go through the trouble of performing a meaningful comparison between a and b. A meaningful comparison is a function that will do one of the following four things:

  1. Return 0 if they are equal
  2. Return 1 if a is greater than b
  3. Return -1 if a is less than b
  4. throw TypeError if no meaningful comparison between a and b is currently defined

Fresh off the script tag, MochiKit knows how to compare primitive value types (undefined, null, string, number), Array-like-objects, and Date-like-objects. However, the compare function is completely extensible and can be convinced into doing whatever you want it to do. This is probably a little strange to most JavaScript programmers, but what MochiKit uses is a form of multiple dispatch using an adapter registry. compare(a, b) works like this:

  1. Check to see if a == b and return 0
  2. Check to see if a or b is undefined or null. If they are both undefined or null, they are equal. If one of them is undefined or null, then the other value is greater.
  3. Ask the comparator registry to perform compare(a, b) if it can
  4. If not, throw TypeError

The comparator registry is effectively a list of function pairs, [check, wrap]. The check function takes two arguments, and returns true if they are comparable with the paired wrap function. The wrap function returns the value of the comparison. When the comparator registry is asked to perform a comparison, it iterates over all of its function pairs in a particular order like this:

for (var i = 0; i < this.pairs; i++) {
    var pair = this.pairs[i];
    if (pair[0](a, b)) {
        return pair[1](a, b);
    }
}
throw NotFound;

Using this model, we can register arbitrary comparisons between anything and anything else! For example, a comparator registry pair for comparing Date-like objects would look like this:

// the "check" function
isDateLike = function () {
    /***

        Returns true if all given arguments are Date-like

    ***/
    for (var i = 0; i < arguments.length; i++) {
        var o = arguments[i];
        if (typeof(o) != "object" || typeof(o.getTime) != 'function') {
            return false;
        }
    }
    return true;
};

// Register a comparator to compare dates
registerComparator(
    // the name of the comparison (for humans)
    "dateLike",
    // the "check" function
    isDateLike,
    // the "wrap" function
    function (a, b) { return compare(a.getTime(), b.getTime()); }
);

Assuming that registerComparitor adds the given function pair to the comparator registry (which it does, and the tests prove it), the dates can now be compared. You can imagine how useful it would be to add comparators for arrays, and your own custom model objects.

Imagine for a minute that you were doing some kind of search engine, and the score was calculated on the client-side, or you were doing some kind of merge from separate result sets, or it was just too much of a bother to sort the results server-side. Making them sortable is no problem with this infrastructure:

SearchResult = function (text, score) {
    this.text = text;
    this.score = score;
};

registerComparator("SearchResult",
    // "check" function
    function (a, b) {
        // this is really "and", but I'm working around an ampersand formatting bug in the blog...
        return !(!(a instanceof SearchResult) || !(b instanceof SearchResult));
    }
    // "wrap" function
    function (a, b) {
        // assume that arrays can be compared meaningfully; they can.
        return compare([a.score, a.text], [b.score, b.text]);
    }
);

// imagine this is populated like so:
var results = [
   new SearchResult(....),
   new SearchResult(....),
   ...
];

// need to sort?  No problem!  Use the built-in Array.prototype.sort!
results.sort(compare);

A different (and often more appropriate) way to filet this fish would be to simply use a key comparator to do this for you, rather than registering a comparator specific to your types. MochiKit has one of those too. The equivalent (given the same definition of SearchResult and results) would be this:

results.sort(keyComparator("score", "text"));

You can imagine how useful this is for doing sortable tables...

2 Comments »

  1. Did you consider some sort of Python to Javascript compiler instead of going through all this pain? Taken to extremes you could end up with something like openlazlo, but it does look like a simpler compiler could work and in the process translate things “right” such as using the correct comparison functions.

    Comment by Roger Binns — 2005-07-05 @ 12:12 am

  2. Whether you’re writing Python that’s translated to JavaScript, or writing straight up JavaScript, this code still needs to be written. There are no comparison functions to translate into. They’re all written from scratch (except for the built-in comparators for primitive value types, of course).

    I don’t really want to write a new langauge right now. That would really distract me from what I’m actually supposed to be doing.

    Comment by Bob Ippolito — 2005-07-05 @ 2:13 am

RSS feed for comments on this post.

Leave a comment

I'm WP-Hashcash. I eat spam.

Powered by WordPress