Monday 24 February 2014

Breaking out of an each() loop

G'day:
I'm just soliciting opinions here. I'll raise a ticket for it anyhow, but let's see what people think.

In Ruby, one can do this:

["Rāhina", "Rātū", "Rāapa", "Rāpare", "Rāmere", "Rāhoroi", "Rātapu"].each_with_index do |v,i|
    break if i > 3
    puts v
end

This is the equivalent of CFML's arrayEach() function (and now the .each() member function). What one can do in Ruby is break out of the loop.

Update:

As Sean pointed out, this is not - strictly-speaking the same as iterating the array and executing a callback on each iteration, as Ruby has this notion of "blocks" which I am using here, and blocks aren't quite the same as callbacks.

So for the sake of completeness, this is equivalent code:

# encoding: utf-8
f = lambda {
    |v,i|
    break if i > 3
    puts v
}
["Rāhina", "Rātū", "Rāapa", "Rāpare", "Rāmere", "Rāhoroi", "Rātapu"].each_with_index &f

I could put the lambda inline, but then it's just back to being a block again. This, incidentally, behaves exactly the same as the other code.

This is not a ubiquitous thing in this sort of looping: it's not allowed in JavaScript:

["Rāhina", "Rātū", "Rāapa", "Rāpare", "Rāmere", "Rāhoroi", "Rātapu"].each(function(v,i){
    if (i > 3) break;
    console.log(v);
});

This errors with:

SyntaxError: Illegal break statement

If you google about the place you'll find lots of discussion about why one might not be able to break out of an each() loop, most of which I find somewhat too dogma-laden for me to take too seriously: "rules for the sake of rules" sort of stuff.

I think it's fine, and Ruby sets a precedent here, so I think it should be fine in CFML as well. However it isn't:

<cfprocessingdirective pageencoding="UTF-8">
<cfscript>
// breakFromEach.cfm
week = ["Rāhina", "Rātū", "Rāapa", "Rāpare", "Rāmere", "Rāhoroi", "Rātapu"];
week.each(function(v,i){
    writeOutput("#v#<br>");
    if (i > 3) break;
});
</cfscript>

This outputs:

Rāhina
Rātū
Rāapa
Rāpare
Rāmere
Rāhoroi
Rātapu


(this breaks in Railo, incidentally, as the index is not passed into the callback).

However one looks at it, ColdFusion is doing it wrong here. It should do one of two things:
  • break when told to do so;
  • error because the break is illegal in that context (as per JavaScript).
ColdFusion mustn't just ignore code.

I think it would be better all round if  the breaks (and continues) were respected in these loops? What do you think?

--
Adam