Big change: lexical scoping
This introduces lexical scoping of for clauses. See README.md
This commit is contained in:
parent
769553832b
commit
2c323be362
5 changed files with 132 additions and 63 deletions
107
README.md
107
README.md
|
@ -1,17 +1,6 @@
|
|||
# goof-loop - a scheme looping facility
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
(loop ((:for a (in 0 b))
|
||||
(:for b (in 1 (+ a b)))
|
||||
(:for count (up-from 0 (to 1000)))
|
||||
(:acc 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.
|
||||
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.
|
||||
|
||||
Compared to foof-loop, some things are added. Apart from minor syntactic changes, subloops are supported. The best way is to show:
|
||||
|
||||
|
@ -26,18 +15,6 @@ Compared to foof-loop, some things are added. Apart from minor syntactic changes
|
|||
|
||||
This will sum all the sublists of lst and produce the result 21. Any :when, :unless, :break, :final, or :subloop clause will break out a subloop if any subsequent for clauses are found.
|
||||
|
||||
Accumulators can be in any of the loop's stages:
|
||||
|
||||
```
|
||||
(loop ((:for a (in-list '(1 2 3)))
|
||||
(:acc aa (summing a))
|
||||
:subloop
|
||||
(:for b (up-from a (to (+ a 2))))
|
||||
(:acc ab (listing b)))
|
||||
=> (values aa ab))
|
||||
;; => (values 6 (1 2 2 3 3 4))
|
||||
```
|
||||
|
||||
## Beta warning
|
||||
|
||||
This is beta quality software, and some minor details are likely to change. I have gotten most kinks worked out though.
|
||||
|
@ -51,6 +28,11 @@ It is written in a weird markdown/xml chimaera. You can find it in documentation
|
|||
|
||||
## Differences from foof-loop
|
||||
|
||||
### lexical
|
||||
|
||||
foof-loop has a lot of code movement going on, and it can be hard to understand exactly where things end up. goof employs a more strict lexical hierarchy. The following is not possible in (chibi loop):
|
||||
d into only after the above clauses have been evaluated.
|
||||
|
||||
### syntactical
|
||||
|
||||
for-clauses are split into :for and :acc clauses. This is because the addition of subloops means we have to treat accumulators differently.
|
||||
|
@ -90,8 +72,20 @@ Due to clause reordering, positional updates are not supported. If you want to u
|
|||
guard was a procedure, but now it is an expression.
|
||||
|
||||
(with var 10 (- var 1) negative?) => (:for var (in 10 (- var 10) (negative? var)))
|
||||
## Features
|
||||
|
||||
### similarities
|
||||
### Lexical order of clauses
|
||||
|
||||
(loop ((:for a (in-list 1 2 3)
|
||||
(:bind b (expensive-operation1 a))
|
||||
(:when (test? b))
|
||||
(:bind c (expensive-operation2 b))
|
||||
(:when test2? c)
|
||||
(:acc acc (listing c))))
|
||||
=> acc)
|
||||
|
||||
|
||||
### Loop naming to make it "fold right"
|
||||
|
||||
You can of course still have a larger control of when to loop by naming your loop:
|
||||
|
||||
|
@ -105,7 +99,7 @@ You can of course still have a larger control of when to loop by naming your loo
|
|||
;; => (-1 4 -9 16 -25 36 -49 64 -81 100)
|
||||
```
|
||||
|
||||
Named updates also work.
|
||||
### Named updates
|
||||
|
||||
```
|
||||
;; Shamelessly stolen from Taylor Campbell's foof-loop documentation
|
||||
|
@ -123,6 +117,61 @@ Named updates also work.
|
|||
;; => (values (1 3 5) (2 4))
|
||||
```
|
||||
|
||||
### Exposing loop variables
|
||||
|
||||
The iterator protocol allows exposing the loop variables
|
||||
|
||||
```
|
||||
(loop name ((:for elt pair (in-list '(1 2 3))))
|
||||
=> '()
|
||||
(if (null? (cdr pair))
|
||||
(list elt)
|
||||
(cons* elt ': (name))))
|
||||
|
||||
;; => (1 : 2 : 3)
|
||||
```
|
||||
|
||||
### :final is context sensitive (compared to Racket's #:final)
|
||||
|
||||
``` scheme
|
||||
|
||||
(loop ((:for elt (in-list '( 1 2 3)))
|
||||
:final (= elt 2)
|
||||
(:for ab (in-list '(a b)))
|
||||
(:acc acc (listing (cons elt ab)))
|
||||
=> acc))
|
||||
|
||||
;; => ((1 . a) (1 . b) (2 . a) (2 . b))
|
||||
```
|
||||
|
||||
The racket counterpart would result in ((1 . a) (1 . b) (2 . a))
|
||||
|
||||
### for-clauses can refer to eachother
|
||||
|
||||
The iterative fibonacci loop is weird to write using for/fold. goof fixes this:
|
||||
``` scheme
|
||||
(loop ((:for a (in 0 b))
|
||||
(:for b (in 1 (+ a b)))
|
||||
(:for count (up-from 0 (to 100)))
|
||||
(:acc acc (listing b)))
|
||||
=> acc
|
||||
(display b) (newline))
|
||||
```
|
||||
### Accumulators and arbitrary code can be placed in subloops
|
||||
|
||||
``` scheme
|
||||
(loop ((:for a (in-list '(1 2 3)))
|
||||
(:acc aa (summing a))
|
||||
(:do (display "Entering subloop!") (newline))
|
||||
:subloop
|
||||
(:for b (up-from a (:to (+ a 2))))
|
||||
(:acc ab (listing b)))
|
||||
=> (values aa ab))
|
||||
;; => 6 (1 2 2 3 3 4)
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Simple forms
|
||||
I also provide simplified forms for many common operations. Omitting :for is allowed, and :acc clauses are not allowed.
|
||||
|
||||
|
@ -161,9 +210,9 @@ I also provide simplified forms for many common operations. Omitting :for is all
|
|||
|
||||
```
|
||||
|
||||
### Speed
|
||||
## Speed
|
||||
|
||||
Speed is good. Despite the rather involved expansion you can see in the documentation, due to dead-code elimination, the actual expansion shows some good code:
|
||||
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:
|
||||
|
||||
```
|
||||
> ,opt (loop ((:for a (in-list '(1 2 3 4)))
|
||||
|
@ -220,8 +269,6 @@ Tests!
|
|||
|
||||
Finish documentation.
|
||||
|
||||
add generator support for all provided iterators
|
||||
|
||||
## 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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue