Generalized JScript

From Ben's Writing

Jump to: navigation, search

A paper submitted as a final project for a co-op work term with Syncrude Canada Ltd. — Arts and Science Co-op [Fall 02 - Summer 03]

Contents

Introduction

Over the past year I have been using JScript more and more in my day-to-day work. As a result I’ve become quite familiar with it and overall, as a scripting language, I have found that it more than meets my needs. When started with it I was looking for a language that, having come from C++, would have a low learning curb, so that I could get started using it quickly. So far it has fit the bill. Unfortunately, while it has been a great tool to work with, it does have its shortcomings. The most astounding of these, in my opinion, is the lack of a flexible container access scheme.

Container access quickly becomes the bulk of any code with any amount of stored data. I'll grant that the schemes provided do fulfill their role, they do allow for iteration, but beyond that their implementation is a bit stiff for my liking. What I was thirsting for was a method for bidirectional and random access, akin to those found in C++. My guess is that they weren’t provided due to efficiency reasons, but the way I figure it is, that if you need efficiency, you shouldn’t be using a scripting language.

The purpose of this essay is to attempt to address this issue and hopefully to propose some acceptable solutions to it.

Goal

To create a framework of common interfaces to enable homogeneous access to all container types, without regard to their respective access methods.

Approaches

Failures

The first hurdle was to figure out what this common access method would be. It sounds kind of silly, but in the beginning I really had no idea of how I was going to do it. I did a lot of research into the problem and finally settled on a few solutions.

Initially it occurred to me that it might be possible to add a set of functions to each container type, via the prototype property, and give them all the same signature. The prototype property enables us to modify the default behavior of an object. Once a prototype is defined, all new instantiations of that object "inherit" — as the online documents put it — the behavior of the function assigned to that object. Doing it this way would have meant that all one would have to do is remember the signature of these simple functions in order to get access to any container. Unfortunately, while this technique worked flawlessly for the built-in containers types, it failed miserably when applied to Dictionary objects and the like. The problem is that, unlike built-in container types, they don’t have a prototype property.

/*
Prototypes work for all built in types: Array, String, etc.
*/

function array_get_at ( index ) { ... }
function array_set_at ( index, value ) { ... }
function array_get_length () { ... }

Array.prototype.get_at = array_get_at;
Array.prototype.set_at = array_set_at;
Array.prototype.get_length = array_get_length;

/*
External types do not support prototypes
*/

var dictionary = new ActiveXObject ( "Scripting.Dictionary" );

dictionary.prototype.get_at = dictionary_get_at;
// issues an error: 'dictionary.prototype' is null or not an object

I could have at this point used JScript's expando properties to extend the external container types. "Expando" properties, sometimes simply referred to as properties, are properties which can be added or removed from an object dynamically at runtime. Using these would have maintained the consistency of function naming, but it would have also meant that the properties would have to be set each time a container was instantiated. In the end I decided to scrap the whole line of thought, not only because it lacked a certain elegance, but because I noticed yet another failing.

/*
Expando properties could be used in place of the prototype properties,
but they would have to be set each time a container of that type was
institated.
*/

var dictionary = new ActiveXObject ( "Scripting.Dictionary" );

dictionary.get_at = dictionary_get_at;
dictionary.set_at = dictionary_set_at;
dictionary.get_length = dictionary_get_length;

The problem shared by both of the above-mentioned approaches is that any additions to an object through the prototype property, or dynamically as an "expando" property, are considered enumerable elements of the object and therefore show up when the container’s content are enumerated by means of a for…in loop or with an Enumerator object. In any case, I found this behavior to be unacceptable, even thought it was inherently part of the language. The thing is, is I didn't think it made any cense for me to limit myself to just using my new access methods, especially considering I might want to integrate the code with an existing project, and I couldn't have it break all code that relied on for…in loops and Enumerator objects.

In the end, what I decided was that I needed a more general approach; one that could be used without regard for container type, whether built-in or not. What I finanlly decided on was an Iterator (Gamma, et al).

Success: Iterators

An Iterator is a pattern, which provides a way of accessing elements of an aggregate object sequentially without exposing the underlying representation

An Iterator was the key; it gave me all the flexibility I was looking for. It allowed me to merge all the access strategies into one entity. Unfortunately, the original problem of the differing access methods exposed by all the container types cropped up and I was still faced with the daunting task of writing heaps of code to cope with each container type. This was completely unacceptable, considering this was the problem I was trying to fix in the first place. So I was back to square one; I needed to find a way simple way to extract the data from each container type.

function Iterator ( accessor, start ) {

        this.accessor  = accessor;
        this.current   = /* ... */
        this.increment = function ( index ) { /* ... */ }
        this.decrement = function ( index ) { /* ... */ }
        this.duplicate = function () { /* ... */ }
        this.equal     = function ( other ) { /* ... */ }
        this.less      = function ( other ) { /* ... */ }
        this.distance  = function ( other ) { /* ... */ }
        this.get_item  = function (){ /* ... */ }
        this.set_item  = function ( value ) { /* ... */ }

}

Fortunately my search was short; why not have an additional level of abstraction, this time between the Iterator and the container. What I needed was an adaptor, which I’ll refer to as Accessors from now on, to fill the role of mediator between the Iterator and the container. This greatly simplified the whole operation but still maintained the systems' flexibility. The Accessors also saved me from re-writing the Iterator object to fit each type, and all that was required for it to access a new container was a small number of modifications.

function Accessor ( coll ) {

        this.collection 	= coll;
        this.begin 		= function () { /* ... */ }
        this.end 		= function () { /* ... */ }
        this.get_collection	= function () { /* ... */ }
        this.get_at 	= function ( index ) { /* ... */ }
        this.set_at 	= function ( index, value ) { /* ... */ }
        this.length 	= function () { /* ... */ }

}

Function Factories

Before I get into the all the new algorithms that can be applied to the containers thanks to the Iterator pattern, I’d like to briefly discus a concept that is used heavily in the implementation of those algorithms.

As the name implies, function factories are function that create new functions. This may seem a bit redundant and you might be tempted to ask why you wouldn’t just create the resulting function yourself instead of having another function create it for you. Well, the new functions aren’t necessarily anything like the originals; they might not even have the same number of parameters nor even return the same type of information. The resulting functions might even be composites of two or more functions, which too could be variants themselves. This might all seem a bit strange but if you’ll bear with me I can assure you it will make sense in the end.

Example:

Little Miss Piggy is writing a program to organize her wardrobe. She wants everything sorted by monetary value. She could have done this by hand, but given that I need an example I figure she's just lazy enough to write code for it. Now she’s got most of this program written, the only thing that is missing is the comparison function. She could have writen something like this:

function clothingCompare ( item1, item2 ) {
        if ( item1.cost > item2.cost ) {
                return 1;
        } else if ( item1.cost == item2.cost ) {
                return 0;
        } else if ( item1.cost < item2.cost ) {
                return -1;
        }
}

This would be a fair attempt but by using function factories we could simplify it even further and write:

var clothingCompare = compose_f_gx_gy ( sort_compare, \
bind2nd ( property_eval, "cost" ) );

At first glance it may not be obvious, but the two pieces of code above describe the same function, albeit the second one involves a lot less code (but is a bit more convoluted, if you’re not used to reading it).

What's happening here is that we are combining already written functions and creating new ones. I'll step thru each part and explain what each one does.

Let’s start with compose_f_gx_gy since it's the most interesting of the lot. What it does is create a new function the returns the results of f(g(x),g(y)), where f and g are functions and x and y are the parameters that are passed to the resulting function. For example, the following would create a binary function that when called would add the negations of two numbers:

compose_f_gx_gy ( plus, negate );

bind2nd is self descriptive; it takes a binary function and creates a unary one by binding the given value — “sort” in the above-mentioned sample — to the function’s second parameter.

property_eval takes two parameters. The first being an object and the second being a property of said object whose value is to be returned.

For the majority of situations these factories are used in conjunction with the algorithm functions I will present later, but there are, as we just saw, few exceptions to this.

Another very important one arises when dealing with Microsoft’s XML parsing engine. The XML Stream object provides the ability to load asynchronously, during which a callback function will be called in order to update the script of the load’s progresses. Unfortunately, the callback does not provide any method by which to access the Stream object itself — ignoring for a second that we could have declared the Stream as a global variable. The problem with this is that the callback gets called for various reasons, not only to signal the completion of the Stream’s task. This information — the reason for the callback — is only accessible thru the stream object itself, but without access to the stream object, the callback is blind to its own parents’ state. Stupid, but that’s they way it is. Fortunately function factories can help us out here.

The following function (factory) will allow us to give the callback function access to the stream object, without forcing us to declare the Stream in the global scope.

function binder ( f, obj ) {
        function fake () {
                return f ( obj );
        }
        return fake;
}

What the function does is create a new function that takes no arguments but returns the result of the function it was given with the argument it was asked to pass.

The reason this function works is because the scope of the external function, binder, remains open while obj and f are still being referenced. The garbage collector — assuming there is one — will not recover the external functions stack until they are no longer referenced.

As an interesting side note, this behavior allows us to declare static variables in JScript (or at least variables that act as though they were static). This is because as we can see from the last example, the external function's stack is preserved as long as the internal function references anything in it.

function test_static () {
        var foo = 0;
        function fake () {
                return foo++;
        }
        test_static = fake;
        return fake ();
}

new function main () {
        for ( var i = 0; i < 10; ++i ) {
                WScript.Echo ( test_static () );
        }
}

Ouput:

0
1
2
...
9

In the example foo is referenced within fake, so as long as fake is being used, foo stays alive. The next trick we pull is to change what test_static actually does. In this case, we make it reference out internal function, fake, and since test_static is part of the global scope by redefining it here we also change its global definition. The end result of all these shenanigans is that foo has now become what could be considered a static variable; it goes thru one time initialization and retains its value between consequent references.

Algorithms

Now that I had a common method of access I could create and apply some generic algorithms to any container. There isn't much to say here as most of the algorithms are derived from C++’s STL but the creating of an Iterator greatly simplified their creation.

For more details on the algorithms you can go to the SGI STL Site

Conclusion

I’m not sure if these techniques will be of use to anyone, they may simply be proof positive that it is possible to write C++ in any language, but I’ve found them to be indispensable.

Other Interesting Sites

Private Members in JavaScript — Not ground breaking, but an interesting read nonetheless. It demonstrates methods or patterns for private members and privileged functions (see article for definitions).

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox