goof-loops aims to be an amalgamation of the racket for loops and Alex Shinn's (chibi-loop). We are many that found racket's for loops a breeze of fresh air, but in the end their most general forms (for/fold and for/foldr) are kinda odd to work with. If you choose not to use those general for loops, you cannot express arbitrary transformations, like say a fibonacci sequence, since for clauses cannot reference eachother. goof-loop tries to fix this.
This is beta quality software, and some minor details are likely to change. I have gotten most kinks worked out though, but if I ever figure out how to include branching, the body-part of the macro will become a :body clause.
It is written in a weird markdown/xml chimaera. You can find it in documentation doc.xml (for the weird format) and documentation/doc.html for the slightly more accessible HTML format.
There is one caveat to this: some accumulating clauses (currently only vectoring with :length specified) have an implicit :break clause. This is tested AFTER the accumulation takes place. So: if the last position of the vector is set, the loop halts.
If you have no "final expression", denoted by => expr, one is added that returns all final values of the :acc clauses in the loop. If none is present, the return value is unspecified.
For clauses which bind "body bindings" (every one except (in ...), in-port, in-generator and in-file) can use pattern matching based on Alex Shinn's excellent match.scm.
Speed is good. Despite the rather involved expansion you can see in the documentation, due to inlining and dead-code elimination, the actual expansion shows some good code:
This used to be a pretty vast collection of examples. goof-loof is now different enough from foof loop that you can't expect to carry your foof-loop skills over to goof-loop. There are however two notable regressions.
### Regressions compared to foof-loop
only accumulating clauses are visible in the final-expression. This is due to sequence clauses not being promoted through to outer loops (since they should not keep their state if an inner loop is exited).
Due to clause reordering, positional updates are not supported. If you want to update your loop vars, do so using named update (see below).
Figure out if I can do anything about branching. I would love to remove the body and just have loop clauses. I don't think I can do that without some serious voodoo if I want to keep the current syntax. One idea would be to define all accumulators in the start of the loop, and then bind identifiers using local macros:
``` scheme
(loop (accumulators ...)
(clauses ...)
=> final-expr)
(loop ((vectoring a)
(listing b))
(:for i (up-from 1 11))
(:save b (* i i))
(:if (odd? i)
(:subloop (:for ab (in-list '(a b)))
(:save a (cons i ab)))
(:subloop (:for cd (in-list '(c d)))
(:save a (cons i cd))))
(:save a 'next))
(1 4 9 16 ...)
#((1 . a) (1 . b) next (2 . c) (2 . d) next ...)
```
But this solution isn't great. The current situation isn't either, though. The body is executed AFTER all other clauses and is only really useful for things like branching, which would be much nicer to have in the clauses. The few times one wants a right fold, a simple :body clause will do the trick.
I have previously expressed some admiration for Alex Shinn and I will do it again. The source of chibi loop is extremely elegant, and all but the hairiest part is written in syntax-rules. Not only has he written my two favourite SRFIs, his input in all the other discussions I have seen is always on-point, pragmatic and generally fantastic. He neither knows of this project, nor embraces it in any way. Y'all should go look at the source of (chibi loop) though.
goof started off by copying the iterator protocol of (chibi loop), and things sort of went downhill from there. Despite this, there is still quite a lot of code (especially in iterators.scm) that I didn't write myself. I only made it ugly. Thus goof is licensed under the same BSD-styled license Alex uses for chibi-loop.