If you are migrating to Haxe (or just porting an existing application) from another C-like language, such as JavaScript or ActionScript, you may have already noticed that for-loops in Haxe differ a bit from standard format that you would expect to see in languages of this type. To be precise, they lack common 3-statement format support. This article is dedicated to different methods of "migrating" your for-loops without rewriting contents entirely.

Standard loops

Fortunately, there is a standard "..." structure in Haxe to allow "for (i = a; i < b; i++)" loops to be ported easily. That means that what would have been this in JavaScript,

for ( var i = 0 ; i < 10 ; i++ ) { trace ( i ) ; }

Becomes this in Haxe,

for ( i in 0 ... 10 ) { trace ( i ) ; }

Non-standard loops

Well, that was an easy case. If you want a loop going backwards, however, this is not as easy. You may want to use an iterator. Noone seems to like using iterators for number-based loops, but not about that. So to convert this JavaScript code

for ( var i = 9 ; i >= 0 ; i-- ) { trace ( i ) ; }

class DecIter { var now: Int ; var limit: Int ; public function new ( max: Int , min: Int ) { now = max; limit = min; } public function hasNext ( ) { return ( now >= limit ) ; } public function next ( ) { return now--; } }

for ( i in new DecIter ( 9 , 0 ) ) { trace ( i ) ; }

We will first need to implement a small iterator like this one:And then you can use it easily:

A downside is, as you may've guessed, that multiple iterator classes will have to be created for different situations.

Update: as it has been pointed out, you can also just invert iterating both variable and range to make a countdown loop.

Talking of iterators

In some cases migrating for-loops is harder. Though it can also happen to be easier than expected. For example, if you're iterating over an array,

for ( var i = 0 ; i < array. length ; i++ ) { trace ( array [ i ] ) ; }

for ( v in array ) { trace ( v ) ; }

the Haxe code is actually going to be shorter than it's JS counterpart:

Similar iterator usage also applies to many standard types including Arrays, Maps (previously Hashes), Enums, certain Reflect methods... overall, if you are looping through contents of something, it's worth checking - it might be already covered "out of box".

Flow control

Now, let's take a look on a slightly rarer case. Let's assume that you want to change the "iterating" variable in the middle of the loop for some reason. For example, to skip a couple of elements from range:

for ( var i = 0 ; i < 10 ; i++ ) { if ( i == 3 ) i += 3 ; trace ( i ) ; }

for ( i in 0 ... 10 ) { if ( i == 3 ) i += 3 ; trace ( i ) ; }

var i0: Int = 0 ; var i1: Int = 10 ; while ( i0 < i1 ) { var i: Int = i0++; if ( i == 3 ) i += 3 ; trace ( i ) ; }

for ( i in 0 ... 10 ) { if ( i >= 3 && i < 6 ) continue ; trace ( i ) ; }

var i: Int = 0 ; while ( i < 10 ) { if ( i == 3 ) i += 3 ; trace ( i ) ; i++; }

"Everything is complicated"

And it would look like you could just do "i += 3" inside of Haxe for-loop as well, but... no, you can't. That's because your for-loop of this kindis actually converted into something of this structure:And thus, changing "i" inside the loop has just about... no good effect.Of course, one can insert a bunch of "continue" statements in this case...Or, as an a better solution, a for-loop can be converted into a while-loop, like this:Now, if you don't look at a small scoping issue, this covers the given case pretty much perfectly... except one point.

Aforementioned code example works pretty nicely as long as you don't insert break/continue statements into your code. If added, such prevent the "post-iteration" code from being executed, and thus may break the program flow.

Indeed, code like this:

for ( var i = 0 ; i < 10 ; i++ ) { if ( i == 3 ) i++; if ( i == 5 ) continue ; trace ( i ) ; }

Is the most complicated case, for which I haven't seen any automatic methods of conversion yet.

However, it is possible to convert/migrate even this. As fact, it is possible to migrate any for-loop... with a bit of hack.

A for-loop, generally, looks like this:

for ( initialization; condition; afterthought ) { action; }

initialization; do { if ( ! ( condition ) ) break ; action; } while ( Std. is ( afterthought, Dynamic ) /* anything that evaluates as "true" */ ) ;

// ... } while ( { afterthought; true; } ) ) ;

var i: Int = 0 ; do { if ( ! ( i < 10 ) ) break ; if ( i == 3 ) i++; if ( i == 5 ) continue ; trace ( i ) ; } while ( i++ >= 0 /* always true */ ) ;

There is initialization, condition, and afterthought. It can be converted into a while-loop, but flow statements like break/continue ruin that. A "do-while" (repeat-until in some languages) loop, on other hand, has a condition checked after the iteration, which makes it more suitable than while-loop. It doesn't have a condition check at iteration start and initialization code, but we can get around that, actually:To clear up how the last line is working: since do-while loop has a "afterthought" condition, and most of operations also have a value returned, you can do whatever operation upon that yielding a "true" result, while having the actual exit condition sit inside the loop. This way the break/continue statements work correctly. If you have been "cool enough" to use a Void-yielding function call as an afterthought, you can also use a lambda expression like this:Thus, the aforementioned JavaScript code can be converted into this:

Which is actually pretty good of approach... don't you think?

Conclusion

Despite of variety of structural presentations, all or almost all code can be migrated. There's no need to rewrite everything from scratch, especially in our age of automation.

Related posts: