Support traditional for() loop syntax

Hi, I really like the new for loop syntax, however it does not allow for multiple iterators easily: eg:

AS3 :

for(var i:int = 0, byte:int = 0; i<len; i++, byte+=8){
}

Haxe :

var byte:Int = 0;
for(i in 0...len){
   byte+=8;
}

For such complex use-cases and also to help port AS3 code it would be useful to support traditional for() loop syntax. Is this possible and are there any plans for this?

Also a Haxe issue. You’ll have better luck asking them.

You could try this: https://gist.github.com/dpeek/7476625.

3 Likes

The for loop haxe extension is amazing, however does it support multiple iterators like the example I showed?

It should, as long as the multiple iterators are separated by semicolons. It looks like it copies the relevant expressions verbatim and appends a final semicolon.

1 Like

You said “seperated by semicolons”, so you mean A or B?

A:
for(var i:int = 0, byte:int = 0; i<len; i++, byte+=8){ }

B:
for(var i:int = 0; byte:int = 0; i<len; i++; byte+=8){ }

So all I have to do is change for to @for and add @:build(FTW.build()) at the beginning of every class?

(Obviously add FTW to my src dir and possibly add an import directive too)

Actually, I mean C:

for(var i:int = 0; byte:int = 0, i<len, i++; byte+=8){
}
1 Like

Complicated. But I get the point. I never noticed that for loops need to have the semis replaced with commas for the extension to work. Thanks for the help!

Turns out, if there are any semicolons at all, you get a compiler error. Try this instead.

Ordinarily, I wouldn’t want to do things to venture away from standard Haxe syntax, but in this case, I sort of wonder if we should consider using something like David’s macro (or yours, @player_03, without a tink_macro dependency) in OpenFL by default. I wonder if there is a way to avoid the macro build, and to more generally apply to user code.

Using @for instead of for obviously requires an opt-in. For this reason, it might not be wrong to include in OpenFL to make it a bit closer to ActionScript in this regard

Dependency removed!

This one isn’t so easy, unfortunately. I tried using Context.onGenerate() to add the build metadata, but unfortunately it crashes before the callback is called. (If only it was called earlier, the macro would fix the error.)

I’ll try looking through all class paths instead, and building the list of classes myself. (Perhaps excluding any that start with “haxe.”)

I suppose that brute-force would be a find/replace as a part of the OpenFL build process, but that would definitely begin to be far-reaching (like @for to Special.for)

Another idea would be something such as Lib.for ()

It’s already a brute-force find and replace within the files in question, so… What we really need is a way to “register” metadata, and have the compiler call a listener whenever “@for” is found.

At this point, Lib.for() might be the way to go. (Edit: Or something with a different name, because for is reserved.)

My own version is dangerous because it won’t always throw an error if you forget the @:build tag. For instance, this runs but only traces “0”:

var i:Int = 0;
@for(i < 6, i++) {
   trace(i);
}

Fortunately, you wouldn’t have to use much code to make this option work:

public static macro function forLoop(init:Expr, condition:Expr, increment:Expr, block:Expr):Expr {
    return macro {
        $init;
        if($condition)
            do $block
            while( {$increment; $condition;} );
    };
}

Unfortunately, it would be farther from the original syntax:

var sum:Int = 0;
forLoop(var i:Int = 1, i <= 100, i++, {
    sum += i;
});
trace(sum);

We could consider hosting it somewhere, like:

Lib.for (var i = 0; i < 100, i++) { trace (i); });

I guess that isn’t quite as consistent since you have to wrap it in parenthesis

I suppose the other option is a standard iterator, like:

for (i in Lib.reverse (100, 0)) { trace (i); }

Not sure of something that would feel like acceptable short-hand

That’s what I was getting at.

Doesn’t help the people who are porting from AS3 or whatever, but it’s not a bad idea in general. I went and wrote an implementation of Python’s range() function. Check it out here.

And in case you’re wondering, the macros allow it to turn this:

for(i in Iterators.range(10, 5, -2)) {
    //...
}

…into this:

var _g_currentValue = 12;
var _g_end = 6;
var _g_step = -2;
while ((_g_currentValue != _g_end)) {
    var i = _g_currentValue += _g_step;
    //...
};

…without a single function call making it into the final code!

1 Like

While we’re at it, what do you think of the from() function? Is there a better name for it?

And more importantly, should I change it so that from(5, 0) iterates over [4, 3, 2, 1, 0] rather than [5, 4, 3, 2, 1]?

Hmm, that’s tricky. I see two perspectives. One says that the first argument is always inclusive, the second is not. The other says that you include the lower value, not the higher.

I think if it was something like Iterators.reverse (0, 5) then I would do 0-4, but Iterators.from (5, 0) should be 5-1, I think, for consistency

I went ahead and added a reverse() function like you described. Any other suggestions?