Thursday, October 30, 2008

Empty iterator still runs _finally

In my years of doing code reviews I have always gotten on people's cases for writing...


_if _not some_set.empty?
_then
_for a_value _over some_set.fast_elements()
_loop
write(a_value)
_endloop
_endif


... what's the point, I asked, of asking SOME_SET if it is empty? If it is empty, then the loop won't be entered anyways and now you are just cluttering up the file with redundant code. It seems that the following should be equivalent...


_for a_value _over some_set.fast_elements()
_loop
write(a_value)
_endloop


Well, today I learned that those two statements are not equivalent. If you use the _finally keyword, its scope will always be processed, even if the iterator has not even run a single loop.


MagikSF> _for a_value _over {:a,1,"bb",300}.fast_elements()
_loop
show(a_value)
_finally
write("Finally")
_endloop
$
:a
1
"bb"
300
Finally
MagikSF> _for a_value _over {}.fast_elements()
_loop
show(a_value)
_finally
write("Finally")
_endloop
$
Finally


So lesson learned... if your _finally block of code expects some value to be set in the main _loop block, be sure to put error handling in there for the case where the loop had no iterations to process.

2 comments:

Brucem said...

Since the scope of the iterator doesn't extend to the _finally, I've never really understood the purpose of the finally statement. I've always wanted it to have the last value of the iterator. I'd love to hear a cogent argument for the use of the _finally clause

Bruce Morehouse
InMaps

Paul Warwicker said...

> I'd love to hear a cogent argument for the use of the _finally clause

An example would be where the loop's processing is not satisfied and a condition should be raised. It is tidier code to raise it in the finally clause rather than handling an unset value (returned from the loop) outside the loop's scope.

lvalue[-tuple] << _for id-list _over iterator-invocation
  _loop
    ...
    # optional early exit
    _if conditional
    _then
      _leave _with rvalue[-tuple]
    _endif
    ...
    _finally
      # purpose of loop not satisfied
      condition.raise(...)
    _endloop