26 June, 2009

Lambdas and closures.

Two of the smartest things about JavaScript are lambdas and closures, which were taken from Scheme.

Why should you care about this? Because, using lambdas and closures, you can do things that would either be impossible or difficult without. An example?

Math.random = (function () {
    var random = Math.random;
    return function (min, max) {
        return max ? (random() * (max - min + 1) + min) : random();
    };
}());

What does this allow you to do? Well, hopefully you know that in JavaScript, you can use Math.random() to return a pseudo-random float between 0.0 and 1.0. This is very useful, of course, as you can then manipulate that value in various ways.

Something I had need of is a function that will return a pseudo-random number within a range. In other words, I wanted to call Math.random(1, 6) to simulate a dice-roll (returning a number between 1 and 6).

"What? How?! You're overwriting Math.random, yet you still need to be able to call it!", you may be saying. That is all true, of course. Even though I have to overwrite Math.random, I still want access to the old version. This is possible (and easy) with closures.

First, I create an anonymous function (lambda) and execute it immediately. Inside I create a reference to Math.random in the variable random. I can now refer to the old Math.random within the anonymous function.

Because I'm assigning this to a variable, whatever the lambda returns will be put into Math.random. In this case, I return a function, which has access to the variable random. This is called a closure.

Now, within the returned function, I can use random to refer to the old Math.random, allowing me to perform all the wizardry I need. Aren't lambdas and closures wonderful?

Here's some examples of how to use it:

// Returns a pseudo-random float between 0.0 and 1.0:
Math.random();

// Returns a pseudo-random float between 5.0 and 8.0:
Math.random(5, 8);

// Returns a pseudo-random int between 5 and 8:
Math.floor(Math.random(5, 8));

Note:  Math.floor is significant. Math.round would produce incorrect numbers.

11 June, 2009

Name change to KAE Scripts.

We are now "KAE Scripts". There were various reasons for this:

  • The previous name lacked identity.
  • It also wasn't very descriptive at all.
  • Besides, why bother having the name "KAE" if you don't use it?

This solves all of the above problems. "KAE" is used in the title, thus providing identity, and "Scripts" implies some sort of scripting language (like JavaScript), giving focus and context.

HTTP.cookie

Chances are, at some point or other you'll have to work with cookies, the little pieces of data sent in HTTP requests.

Unfortunately, the way to work with cookies in JavaScript is absolutely horrible. Compounding the problem, there isn't any standard easy way to get/set a cookie, so we end up with dozens of different functions, each trying to do exactly the same thing.

Even worse, a lot of these functions (though they work), are very verbose and inefficient, making them both unwieldy and slow. If that wasn't bad enough, they often pollute the global namespace (sometimes with 3 global functions!).

As awful as dealing with cookies is, I found myself needing to do this. I had some criteria that I wanted in a cookie function:

  • Support getting/setting/deleting cookies.
  • Allow all the cookie settings, like path, domain, max-age, etc.
  • Use the same function for everything.
  • Lightweight in terms of code, and also very fast.
  • Use at most one global variable, preferably none.
  • Not tied to any libraries, making it easy to embed.
  • Simple and intuitive to use.

Nothing fulfilled all the above requirements, so I implemented my own. It uses the HTTP global variable, in case somebody wants to add another method dealing with HTTP.

You use it like this:

// Get a cookie:
HTTP.cookie("test");

// Set a cookie:
HTTP.cookie("test", "Hi!");

// Delete a cookie:
HTTP.cookie("test", null);

In addition, you can pass along a third parameter, which lets you set the special cookie settings:

// All the settings at once:
HTTP.cookie("test", "Hi!", {
    path: "/",
    domain: "temp",
    maxAge: 31536000,
    expires: new Date(2010, 0, 0),
    secure: true
});

Every setting works as you would expect. As per the specification, expires should be a GMT-string. You can pass in a string directly, or a Date object, which will then be converted into a GMT-string.

NOTE: If you set a cookie with path, domain, or secure, you must delete the cookie with the same settings. In other words: two cookies with the same name and value (but different settings) are considered as two different cookies.

[LINK] The source code.
[LINK] The unit tests.

References:
[MDC] document.cookie

08 June, 2009

arguments.callee

ECMAScript 5 strict mode has completely removed arguments.callee.

Now, there have been very good arguments both for and against arguments.callee, and I'm not going to go into them. However, what I will go into is how it changed my coding style, and also some possible perks.

Let's take an example of code that I showed in an earlier post:

var alert = function () {
    var that = arguments.callee;
    if (!that.stop) {
        that.stop = !confirm(Array.prototype.join.call(arguments, "\n"));
    }
};

Now, there isn't anything particularly wrong with this code: it works fine, and passes JSLint. However, it will surely fail in strict mode. Here is how I corrected it:

var alert = function anon() {
    if (!anon.stop) {
        anon.stop = !confirm(Array.prototype.join.call(arguments, "\n"));
    }
};

The above does the same thing, but without using arguments.callee. How is this possible? It uses a named anonymous function. Yes, yes, it's an oxymoron, but please bear with me here.

Most anonymous functions are.. well, anonymous. If you want to do any kind of recursion, you have to either name the function (polluting the namespace), or use arguments.callee.

However, in later versions of ECMAScript, we are given the ability to make a named anonymous function. This is like a combination between a named function and an anonymous function, but with a twist: the name is only usable inside of the function.

Let's test that with some code:

var foo = function bar() {
    alert(bar);
    alert(window.bar === bar);
};
foo();

Note how the anonymous function bar() is accessible within itself, but does not create a global function bar(). In addition, if we assigned this function to a different variable, bar() would still be intact!

Using this technique, we no longer have any need for arguments.callee. We can use bar() within the function to refer to itself, and nothing outside has access to bar().

An interesting side effect of this is that alert.stop is undefined. In the old way you could access alert.stop. That turned out to be harmless; but in a more critical function it could have had disastrous effects.

The primary argument against this change is that it breaks stuff in IE. Except that IE has always had severe problems. It should work just fine in any non-IE browser. Also note that this only matters if you are using strict mode. If you are in normal non-strict mode, you can still use arguments.callee.

Either way, I plan to use this technique much more often, as it has proven it's use.

02 June, 2009

Game of Life in JavaScript.

[LINK] Conway's Game of Life

I have been fascinated by the Game of Life for some time now, and recently had the idea to make it in JavaScript.

Although the idea is hardly new, I am hoping that my version will end up being faster, better, and easier to use than the others. Try selecting a Template, then hit the Start button.

NOTE: I am using canvas for performance reasons. As such, it won't work in Internet Explorer; but I am considering fixing that.

UPDATE: I have officially given up supporting this in IE. Microsoft has gone out of their way to not support the W3C Event model, despite being given plenty of time. So, until a future version of IE decides to support the standardized events: tough luck.