First commit
This commit is contained in:
commit
ef96da9658
4 changed files with 855 additions and 0 deletions
94
README.md
Normal file
94
README.md
Normal file
|
@ -0,0 +1,94 @@
|
|||
# goof-loop - a scheme looping facility
|
||||
|
||||
WARNING: CURRENTLY PRE-ALPHA
|
||||
|
||||
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.
|
Loading…
Add table
Add a link
Reference in a new issue