goof.scm | ||
helpers.scm | ||
iterators.scm | ||
LICENCE | ||
README.md |
goof-loop - a scheme looping facility
WARNING: CURRENTLY PRE-ALPHA. The examples in this document are not consistent with the current direction I am pushing this (even though they should work.
goof-loops aims to be an amalgamation of the racket for loops and Alex Shinn's foof-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. This is understandable given how they are tied to the underlying racket sequences, but still somewhat disappointing. goof-loop tries to fix this:
(loop ((:for a (in 0 b))
(:for b (in 1 (+ a b)))
(:for count (up-from 0 (to 1000)))
(:for acc (listing b)))
=> acc
(display b) (newline))
The above example will display and accumulate the 1000 first fibonacci numbers. Doing the same thing in racket requires you to manually handle all the state in fold-variables using for/fold. It is a simple example, but proves the usefulness of goof-loop.
Compared to foof-loop, some things are added. Apart from minor syntactic changes, subloops are supported. The best way is to show:
(define lst '((1 2) 'dud (3 4) (5 6)))
(loop ((:for a (in-list lst))
(:when (pair? a))
(:for b (in-list a))
(:for acc (summing b)))
=> acc)
This will sum all the sublists of lst and produce the result 21. Any :when, :unless, or :break clause will break out a subloop if any subsequent for clauses are found.
Differences from foof-loop
syntactical
all keywords are prepended with a : to distinguish them from regular variables. for -> :for
while and until are removed in favour of :break.
:when and :unless are added to better control when the loop body is executed (and accumulators accumulated)
with-clauses are removed in favour of (:for var (in init [step [stop]]))
accumulators are no longer for-clauses, but should be prepended with :acc.
Regressions
only :acc clauses are visible in the final-expression. This is due to for-clauses not being promoted through to outer loops (since they should not keep their state).
:for clauses cannot finalize, due to the above thing. The reason for distinguishing between :for and :acc is to be able to promote accumulators outwards and finalizers inwards. This is not implemented.
changes
(with var [init [step [guard]]]) => (:for var (in init [step [stop-expr]])). guard was a procedure, but now it is an expression.
(with var 10 (- var 1) negative?) => (:for var (in 10 (- var 10) (negative? var)))
I plan to remove non-named variable updates. That is a minor inconveniance, but unnamed updates has been my largest source of bugs, so I have grown to hate them.
similarities
You can of course still have a larger control of your loops:
(loop loopy-loop ((:for a (up-from 1 (to 11))))
=> '()
(if (odd? a)
(cons (* a (- a)) (loopy-loop))
(cons (* a a) (loopy-loop))))
;; => (-1 4 -9 16 -25 36 -49 64 -81 100)
Named updates have a bug, sadly.
Todo
Currently, there is a bug if you have subloops more than 2 loops deep where all accumulators are reset. This should be an easy fix.
Regarding the above: fixing that bug does nothing! I can only output loops of at most 2.
Should we add finalizers for :for-clauses? I can't see the need outside of a potential (in-file ...), which can't be properly supported anyway since I won't do any dynamic-wind stuff.
Is (:for var (in init step stop)) and (:acc var (in init update)) good syntax? the :with clause of foof-loop is nice, but what should it be called for accumulators? Should we go back to calling both :acc and :for just ":for" and re-add :with and an accumulating counterpart? What should that accumulating counterpart be called? :acc?
Add racket #:final clauses.
foof, what a guy
I have previously expressed some admiration for Alex 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.
Licence
The same BSD-styled license Alex uses for chibi-loop.