Sunday, March 25, 2007

Don't Repeat Yourself: or Javascript DOM Initializers

I think I did something clever. Once again, it's time to fight the losing battle of explanation. I find that I've been repeating myself a lot in Javascript. Worse, I've had to compromise my ideals pretty frequently. Consider the following Javascript.

<html>
	<head>
		<script src="http://cixar.com/javascript/modules.js">
	</head>
	<body>
		<div id="clock_0"></div>
		<script>
			require('clock.js', function (clock) {
				clock.Clock(
					document.getElementById('clock.js')
				);
			});
		</script>
	</body>
</html>

In this example, I've installed the Cixar Javascript module loader. This gives me a global function called require that fetches javascripts and loads them as singleton modules and returns the module object or calls a given function closure with the module object. I then populate my document with HTML and bless the HTML with Javascripts. In this case, I set up a clock. The details of how this clock works are hidden in a Clock type that is in turn hidden within a clock module. This particular setup requires that any blessed DOM element have an identifier so that it can be in turn fetched by name in the Javascript.

It turns out that this pattern of laying out out a DOM element like a div followed by a script to imbue it with DHTML powers is pretty common in my Ish project. This makes for a lot of script blocks but also makes the server side code rather orthogonal and elegant. It's not that bad, and I am proud it's this clean so far.

However, it could be much cleaner. Consider this example that I implemented today.

<html>
	<head>
		<script src="http://cixar.com/javascript/modules.js"></script>
		<script>require('init.js')</script>
	</head>
	<body>
		<div require="clock.js#Clock"></div>
	</body>
</html>

Observe that with this approach, the div element does not have an identifier nor a corresponding script block. Instead, I've loaded an init.js module (the name will change when you email me a better one). The init.js module sets up a page load listener. When the page loads, the initializer scans the entire DOM for elements with require or requireRelative attributes. It then loads the given module, extracts the given function from the module, and blesses the corresponding element with that function.

I suspect this has a hidden advantage in addition to elegance. With the previous strategy, every DOM element had a corresponding getElementById call. I suspect that getElementById traverses the DOM until it finds an element with the given ID (plausibly, the browser tracks an (id, element) mapping dynamically, but this would be tricky to implement correctly). The old approach requires one of these traversals for every blessed DOM element. This new approach only requires one traversal of the document per page load.

So, two questions for the audience. What should this module be called instead of init.js? Alternately, should this behavior be automatically installed by modules.js?

Thursday, March 22, 2007

Winged Monkeys

Just kidding. There aren't really any winged monkeys. However, there are going to be some other flying animalic contraptions in Tale: Gyro-pig and Lamb-chopper.

Also, I've posted some graphics for the faces of the Tale world.

Wednesday, March 14, 2007

Programming in Javascript

I'm going to attempt to explain computer programming. This will be interesting for my sister since she's nominally curious about the topic, and as I stumble through increasingly absurd (but commonly accepted) metaphors, I'll accidentally illustrate how odd the mind of a programmer must really be.

Programming is like writing a recipe book.

/* this is a recipe */
var ingredients;
mix(ingredients);
cook();

Running a program is like having a toddler make a gourmet meal by following the instructions in your recipe book (or toddlers in any number of kitchens capable of communicating with each other over the phone in ostensibly incomprehensible sequences of "gah" and "goo-goo" that mysteriously can also convey food). The recipe book is also a "make your own ending" novel. As the toddler reads instructions it will find "branches" like, "Making bread, step 567: to apologize to the butler, go to chapter 10. To bludgeon the butler with a monkey-wrench, go to chapter 829." However, "branches" or "gotos" are fairly uncommon.

/* this is not Javascript, but it looks a bit like it */
if (choice == 'apologies') {
	goto apologies;
} else {
	goto bludgeon;
}

Recipe writers use "functions" instead. A function is a chapter in the recipe book, a mini-recipe, wherein the author provides detailed instructions for doing things like "beat an egg". You can tell an infant to go to any of these chapters to do some particular task and then go back to the part of the recipe where they started, like "Making bread, step 568: knead dough. Go to chapter 12 with this dough and bring it back to step 569 when you're done."

var bread = function () {
	var eggs;
	var flour;
	var dough;
	dough = mix(eggs, flour);
	knead(dough);
	bake(dough);
	return dough;
}

To keep track of where they are in the recipe, the toddler keeps a stack of dishes, upon which, the toddler arranges peas and carrots with chapter, page, and step numbers from the recipe book.

I'm making bread
	I'm mixing eggs with flour
		I'm breaking an egg
			I'm picking bits of shell out of the yolk

This stack of dishes is often very tall.

With Javascript, this stack is usually about 10 plates. Apple's web browser, Safari, lets you stack 100 plates. Firefox's implementation of Javascript lets you juggle a stack of 1000 plates. Microsoft's Internet Explorer 6 handles 1125 plates. Opera gives you room for a whopping 3345 plates.

You might wonder why the toddler (in this case Hansel and Gretel) can't arrange the peas and carrots on the recipe book itself whenever it gets to a new chapter, letting it know where it needs to go back. There are some tricky chapters that actually, and often by way of long paths, refer back to themselves. These are called "recursive" functions and they have the special attribute that they must be "re-entrant", meaning that any number of toddlers can have any number of plates referring back to it without getting lost in their recipes or mixing up each other's ingredients. Recursion is handy because you can write chapters like "Find the spoons" that go like this: "If there's more than one place to look, divide those places in half. Find the spoons in north half. If you don't find them there, find the spoons in the south half. If you don't find them there, cry." Seriously, it's handy, and if you write the chapter well enough, it'll go a lot faster than, "Look in a new place. If you don't find the spoons, try again."

var find = function (places, spoons) {
	if (count(places) > 1) {
		try {
			find(beginHalf(places), spoons);
		} catch (tantrum) {
			find(endHalf(places), spoons);
		}
	} else if (has(places[0], spoons)) {
		return places[0];
	} else {
		throw new Tantrum("Wah!");
	}
};

The next nice thing about functions is that the recipes don't have to be particularly specific. This means you could write a chapter in your recipe book entitled, "Find Something". That way, the recipe writer can call for the toddler to "Find Spoons", "Find Monkey-wrench", or "Bludgeon Butler with Monkey-wrench". Writing recipes is about being vague. You never know when you're going to need to do something again and you don't want to write out every detail.

var bludgeon = function (something) {
};

So, you're keeping track of a lot of ingredients, so knowing what to call them can be a bit of a bear. You want to be sure that when you call for "the apple", you don't just get any apple; it has to be the relevant apple that the toddler just skinned. Most of the time, you keep track of names with the outline of your recipe. If you refer to an "apple", you read backwards first in the chapter, then the book the chapter is in, then the volume. You don't look in other chapters, books, or volumes because they might have their own apples.

var volume = function () {
	var apple = new Apple();
	var chapter = function () {
		var orange = new Orange();
		var step = function () {
			compare(apple, orange);
		};
	};
};

Many languages leave it at that. However some languages have "closures". It's a weird word for a simple idea. A closure is where you keep track of the plate you were on when you read the function. Sometimes reading a function is part of the recipe. When you do that, you're making it so that when you call that function, you can use any of the ingredients on your plate.

var makeACounterFunction = function () {
	var whereImAt = 0;
	return function () {
		var whereIWas = whereImAt;
		whereImAt = whereImAt + 1;
		return whereIWas;
	};
};
var counterFunction = makeACounterFunction();
assert(counterFunction() == 0);
assert(counterFunction() == 1);
assert(counterFunction() == 2);

Some languages are object oriented. There are a lot of interesting things about object oriented programming, but the short of it is that your ingredients can each have their own recipes and sub ingredients. You don't have to tell the function which ingredient to work on. You can just talk about it as "this".

var Egg = function () {
	this.break = function () {
		break(this);
	};
};
var egg = new Egg();
egg.break();

So, I'm going to gloss over a few details and descend into madness now.

Javascript Module Loader

I had an epiphany when writing a module loader. The trouble is that I didn't want modules to have access or ability to mess with the module loader's local variables. Since I was just calling eval with the text of the module, it had all of this access for reading and writing. Since I don't trust even myself, this was not acceptable. The problem was finding a way to sweep all the names under the carpet.

Here's the trivial solution:

(function () {
	/* module loader */
	this.require = function (url) {
		var require = function (url) {
			/* this require is for the module */
		};
		var text = http.requestText(url);
		eval(text);
	}
})();

Here's a less naive solution:

var evalModule = function (text, require) {
	eval(text);
};
(function () {
	this.require = function (url) {
		var text = http.requestText(url);
		var require = function (url) {};
		evalModule(text, require);
	};
})()

That solution still leaves evalModule as a variable that the module could potentially supplant. I needed to isolate evalModule. My solution uses a member variable of a jail object which eventually gets populated with the evalModule function without giving the closure access to either jail or itself..

(function () {
	var jail = {};
	this.require = function (url) {
		var text = http.requestText(url);
		var require = function (url) {};
		evalModule(text, require);
	};
	return jail;
})().evalModule = function (text, require) {
	eval(text);
}

Of course, it's not enough to isolate the module in a closure. I also give it a Module object as its context object.

(function () {
	var jail = {};
	this.require = function (url) {
		var text = http.requestText(url);
		var require = function (url) {};
		var module = new Module(url);
		evalModule.call(module, text, require);
	};
	return jail;
})().evalModule = function (text, require) {
	eval(text);
}

So, modules look like this:

var module = this;
assert(this != window);
with (require('module.js')) {
	/* go do stuff without fear of name collisions */
}

Javascript Functions, Accelerated Introduction

In Javascript, functions don't really have intrinsic names. The name is just a variable you've assigned an anonymous function object to.

function a () {}
/* is the same as */
var a = function () {};

You can tell a function that it should use some other object as "this".

var egg = new Egg();
var weasel = new Sweater();
egg.break.call(weasel);

You can also send any number of arguments to the function, even if it doesn't ask for them. Then, inside the function, you can get at all the extra arguments with the arguments variable.

(function (a, b) {
	assert(a == 10);
	assert(b == 20);
	assert(arguments[1] == 20);
	assert(arguments[2] == 30);
}).call(this, 10, 20, 30);

You can send arguments to a function from an Array.

(function (a, b, c) {
	assert(a == 1);
	assert(b == 2);
	assert(c == 3);
}).apply(this, [1, 2, 3]);

You can even make up an array of arguments with a program.

var array = [1];
array.push(2);
assert(array == [1, 2]);
var sum = function (a, b) {
	return a + b;
}
assert(sum.apply(this, array) == 3);

You don't have to give a function all of the arguments it asks for.

(function (a, b, c) {
	assert(a == 10);
	assert(b == 20);
	assert(c == undefined);
})(10, 20);

You can run programs/recipes that you get as quoted data.

eval("var a = 10; a = a + 1");

When you eval, you can see all the data in the caller's closure.

var a = 10;
eval('a = 20');
assert(a == 20);

An interesting thing about putting Javascript in HTML is that all of the scripts have the same closure.

<script>
	var global = true;
<script>
<script>
	assert(global);
<script>

You can wrap a bit of code in a closure so that its names are all its own, can't conflict with other scripts.

var global = true;
(function () {
	var local = true;
})();
/* local is not visible here */

In a web browser, the context object for the entire program is the Window object containing the script.

assert(this == window);
(function () {
	assert(this != window);
	assert(this == 10);
}).call(10);

The window is also the base closure for your program, so you can access everything in it as if it were a local variable.

window.document == document

You can play this trick with any object's member data or functions using the with block.

with ({'a': 10}) {
	assert(a == 10);
}

Thursday, March 1, 2007

Photo Rally 3 - Continued

The photos for Photo Rally 3, from all teams, are now available online.