Closures Cure Global Pollution

| 2 Comments

A point illustrated in two digressive examples.

Plack is Silly Simple

I gave a presentation to the Portland Perl Mongers earlier this month, in which I explained just how simple Plack is:

  • A web application is a function reference.
  • That function reference takes a single parameter, a Plack::Request object.
  • That function reference returns a single result, a Plack::Response object.

It's so simple, it's silly. At least it seems silly. What's the advantage over something like mod_perl or CGI or FastCGI?

Composability and middleware.

Composability

I spend part of my time consulting with a couple of friends on a project named ClubCompy. They've built a virtual 8-bit computer in JavaScript and HTML 5—you don't have to install any software besides a modern web browser to get a simple programming environment. (Obviously building more exciting programming environments is possible, but the approach is didactic, favoring simplicity and text-based programming over something like Scratch.)

ClubCompy's code takes advantage of JavaScript's advantages and tries to avoid JavaScript's disadvantages. All of the classes in CC attach to a single god object which is a global variable named ClubCompy. If you want to instantiate another object, you access its name through a property on the ClubCompy object. This is all well and good—it reduces contamination of the global namespace.

Except that there's still contamination of the global namespace.

CC has a rudimentary inclusion system modeled somewhat after the C programming language's #include system. When you include a file, you must #define a symbol which you check as a flag to make sure you don't include a file again. In JavaScript this looks something like:

if (!ClubCompy.script_bufferedChar)
{
    ClubCompy.script_bufferedChar = true;	

    ...
}

Every included script (because you don't want tens of thousands of lines in a single file in any programming language) has a similar guard.

One obvious improvement is:

ClubCompy.scripts = {};

if (!ClubCompy.scripts.bufferedChar)
{
    ClubCompy.scripts.bufferedChar = true;

    ...
}

... which has its obvious parallels to Perl 5's %INC and could ggeneralize further into something more like:

ClubCompy.require( 'bufferedChar' );

... to manage this information.

The inclusion mechanism in ClubCompy wraps the contents of each included file in an anonymous function so as to protect the global namespace. That is to say, each individual file can declare its own lexical variables without worrying that those lexical variables leak out elsewhere. This is well and good.

Then Dave said "It's too bad we can't have multiple ClubCompy instances running on a page."

Remember how I said Plack is silly simple?

On Naming Anonymous Things

ClubCompy currently only lets you run one instance on a page because the ClubCompy object is global. You get one. That's it. There are no more. This object is global so that everyone can refer to it by name without worrying about how to access it.

That's why people make things Singletons, right?

If the ClubCompy object weren't global, there'd be no problem running multiple instances of ClubCompy on any given page, because they wouldn't be in conflict—but how do you find a global object if it's not global?

I belabor the point (many of you already know the answer) to explain one in terms of the other as well as a design principle in terms of both. In JavaScript, can you tell if ClubCompy is a global variable in this expression:

ClubCompy.renderFrame(frameObj);

It's easier in this snippet:

var render = function (frameObj)
{
    ClubCompy.renderFrame(frameObj);
}

In truth, the JavaScript compiler doesn't really care. You could just as well:

var render = function (cc, frameObj)
{
    cc.renderFrame(frameObj);
};

Or in Perl 5:

my $render = sub
{
    ClubCompy->render_frame( shift );
};

... versus:

my $render = sub
{
    my ($cc, $frame) = @_;
    $cc->render_frame( $frame );
}

Perl doesn't care whether the invocant is a hard-coded string or a global variable or a lexical scoped outside of the block or a lexical parameter passed to the function. Likewise, JavaScript will look up the symbol according to its regular rules.

In other words, allowing ClubCompy to run multiple instances per page requires only changing the binding of the ClubCompy symbol within the libraries which use it. If the top-level code which creates a new ClubCompy instance does so without polluting the global namespace, top-level code can create multiple instances which do not conflict at the global namespace level.

The loader which wraps all of these included JavaScript files in an anonymous function turns from:

var includedFile = function ()
{
    # ... included text here
};
    
includedFile();

... to:

var includedFile = function (cc)
{
    var ClubCompy = cc;

    # included text here
};

includedFile( nonGlobalClubCompy );

... and everything else just works.

Behold the magic of lexical variable encapsulation and the magic of closures—because a ClubCompy instance on a page is just a function reference.

2 Comments

I thought Plack was even simpler, not requiring any objects at all. A Plack web app is a function reference that takes one argument (a hash containing the environment and streams) and returns a single result (an arrayref containing the HTTP status, the HTTP headers and the response body as a string or IO stream.

The PSGI specification does not require objects. Plack provides helper request and response objects. I waffle on how to explain Plack/PSGI in the most useful and most easily understood way.

Modern Perl: The Book

cover image for Modern Perl: the book

The best Perl Programmers read Modern Perl: The Book.

sponsored by the How to Make a Smoothie guide

Categories

Pages

About this Entry

This page contains a single entry by chromatic published on May 25, 2011 1:51 PM.

Count the (Possible) Bugs in this Code was the previous entry in this blog.

Summer 2011 Conference Talks is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.


Powered by the Perl programming language

what is programming?