Prefix - ForEach Statements

Thursday, September 29th, 2011     #javascript #everything

Prefix now supports foreach loops but their not exactly as you might expect....

This post is about Prefix - a modern language that compiles to JavaScript. A language where C# is the inspiration - not the goal. Read more on the Prefix Project Page.

Prefix has had for loops pretty much since day one. I'd deliberately left out foreach loops until now because I wanted to integrate them with some sort of enumerable/generator (eg: IEnumerable<T>) mechanism, but I've decided to put that off for now and just aim for parity with JavaScript functionality.

Enumerating Array Like Objects

First up, the Prefix foreach statement will work with any type that implements a int length variable or property and an indexer that takes an integer parameter - including obviously arrays and strings.

Prefix uses a temporary variable as the counter and just enumerates the collection as you'd expect:

{{C#}}
extern dynamic Console;

var x = new string[];
x.push("Hello");
x.push("World");

foreach (var i in x)
{
 Console.WriteLine(i);
}
{{JavaScript}}
(function() {

// Start Global Code
var $temp;
var x=[];
x.push("Hello");
x.push("World");
for ($temp=0; $temp<x.length; $temp++)
{
   var i = x[$temp];
   Console.WriteLine(i);
}

})();

The generated code will use an additional temporary variable if the expression begin enumerated might cause side effects. In this example, $temp2 is used to hold the array to avoid multiple calls into fn:

{{C#}}
extern dynamic Console;

string[] fn()
{
    var x = new string[];
    x.push("Hello");
    x.push("World");
    return x;
}

foreach (var i in fn())
{
    Console.WriteLine(i);
}
{{JavaScript}}
(function() {

function fn()
{
    var x=[];
    x.push("Hello");
    x.push("World");
    return x;
};

// Start Global Code
var $temp, $temp2;
for ($temp=0, $temp2=fn(); $temp<$temp2.length; $temp++)
{
    var i = $temp2[$temp];
    Console.WriteLine(i);
}

})();

(in case you're wondering, Prefix tracks temporary variable allocations and re-uses them, so multiple consecutive loops like this will declare the temporary variables once at the top of the scope (function) and re-use them multiple times over).

Enumerating Dictionaries

I've always thought object enumeration in JavaScript to be a little messy - though I do understand the reasons for it. In JavaScript there's two main reasons for enumerating an object - to get all the values of a associative array, or to enumerate all the methods, properties, fields of an object (say to dump/inspect it). The differences with these two approaches relates to whether to enumerate the prototype chain or not.

The JavaScript for...in statement does enumerate the prototype chain. If you only want the immediate properties of the object (say the keys of a dictionary) you need to use the hasOwnProperty method to detect the items to exclude.

In Prefix, the call to hasOwnProperty is included automatically in generated foreach loops.

(In the following example I'm using backtick escapes to declare literal JavaScript - to better show the object layout).

{{C#}}
extern dynamic Console;

// Purposely put something that doesn't belong in the object prototype
// This SHOULD NOT appear in the output
`Object.prototype.somecrap = 23`;

// Create a dictionary
var dict = `{apple:1, pear:2, banana:3}`;

// Dump the contents of the dictionary
foreach (var i in dict)
{
    Console.WriteLine(i);
}
{{JavaScript}}
(function() {

// Start Global Code
Object.prototype.somecrap = 23;
var dict={apple:1, pear:2, banana:3};
for (var i in dict)
{
 if (!dict.hasOwnProperty(i))
        continue;
   Console.WriteLine(i);
}

})();

Enumerating Object Members

The above example raises the obvious question of how to enumerate the entire prototype chain? Well, with a new form of the foreach statement: foreach member (var i in whatever).

The following example is the same as the above but I've added the member pseudo-keyword:

{{C#}}
extern dynamic Console;

// Purposely put something that doesn't belong in the object prototype
// This SHOULD appear in the output
`Object.prototype.somecrap = 23`;

// Create a dictionary
var dict = `{apple:1, pear:2, banana:3}`;

// Dump all members of the dictionary (including the extra property in the object prototype)
foreach member (var i in dict)
{
   Console.WriteLine(i);
}
{{JavaScript}}
(function() {

// Start Global Code
Object.prototype.somecrap = 23;
var dict={apple:1, pear:2, banana:3};
for (var i in dict)
{
 Console.WriteLine(i);
}

})();

The member modifier can also be used on arrays to enumerate the actual members of the array instead of just the items in the array.

Conclusion

That's all for the foreach statement for the moment. I do intend on addition support for something like IEnumerable<T> and yield return but not just yet...

If you've been using the experimental build of Prefix, I haven't updated it with this functionality yet - I'll post something when I do.

« Older - Change of Plans, Prefix Shelved. Newer - Prefix - Anonymous Functions and Lambdas »