Infinite Loops vs. Errors


There's a seemingly innocuous response to an edge case in FOR in R3-Alpha:

>> for i 1 10 0 [print "???"]
== none

That's a change from Rebol2, where it would infinite loop. The change was deliberate, and is part of a discussion that wanders into design philosophy:

REBOL-ISSUES: FOR start end bump behavior inconsistent, doesn't make sense

BrianH writes:

My two proposals were both intended to make absolutely sure that no combination of start, end and bump would by themselves end up with FOR doing an infinite loop. The main purpose of this is to allow the developer to remove expensive conditional code that would be needed to screen start, end and bump in combination to make sure that such loops don't happen by accident.

He defends the idea of just returning vs. raising an error when a 0 is given as a bump amount:

Of my two proposals, one has it trigger an error when bump = 0 and start != end, and one has the loop just not do anything and return none. I like the second of those choices, because you could plausibly argue that we have decided that a bump of 0 is just out of range when start != end. That will allow the developer to cut down on conditional code to avoid errors, which is the whole reason for defining 0 to be out of range in the first place. So it would be counterproductive to require them to add back the conditional code for another reason.

He argues that the existence of FOREVER means people wishing to intentionally write an infinite loop can do that another way.

It's an idea...but is it a good one?

No-infinite-loops is a premise that bears scrutiny. Consider also how R3-Alpha's PARSE tries to avoid infinite loops by failing out "if it doesn't make progress". This rule has caused frustration because its detection of progress is based on the position moving, and rules that delete material but stay in one place for the next rule don't trigger that detection.

If I put on my code golfing hat, it makes sense to not error on inputs that could be given a legitimate meaning. So refusing to do something on instances like SKIP of 0 doesn't seem like a good idea.

But a blank/null result and no loop from something that should have been an infinite loop could confuse people. And just because FOREVER exists doesn't mean that you might not want to write one "polymorphic" loop that could decay into an infinite loop if necessary. Plus infinite loops aren't necessarily infinite, you can BREAK out of them.

Consider also that Ren-C uses BLANK! as a way of opting out of an operation by way of its arguments. So if you are passing in indices to FOR and want to have a bump value you can pass in to get the loop to not run at all, that could be a good role for BLANK!. And you can turn zeros into blanks pretty easily (maybe in the future, with try match :(not zero?) arg)

So when one is trying to encourage simplicity, are complex "protections" against infinite loops adding value...or just making the system more mysterious? My feeling at the moment is that it would be better to go ahead and do what people say in the "traditional" way with infinite loops, and let improved debugging pick up any slack.


Doing a clever hidden control will not prevent a lot of people of adding a useless check because they don't understand, forget the hidden rule, or are anxious.

It's just like optimising compiler. It's prematured and misinformed optimisation.

The code emitted should always be the same for the same code written. An invariant emitter/interpreter is the only sane way for a developer to code. All must be damn explicit. Only the end developer can take a right decision for his case, and take the responsibility.

So, keep the infinite loop is my opinion.