Changed documentation to reflect recent changes

This commit is contained in:
Linus 2021-05-21 10:09:07 +02:00
parent 93134a1b21
commit dd1589ab3a
3 changed files with 244 additions and 117 deletions

View file

@ -41,6 +41,8 @@ It is written in a weird markdown/xml chimaera. You can find it in documentation
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.
This of course also means accumulation is done before the body is executed, which is a bit counterintuitive.
``` scheme
(loop ((:for a (in-list '(1 2 3)))
(:acc vec (vectoring a (:length 2)))
@ -163,12 +165,15 @@ The iterative fibonacci loop is weird to write using for/fold. goof fixes this:
:subloop
(:for b (up-from a (:to (+ a 2))))
(:acc ab (listing b))))
;; |> Entering subloop!
;; |> Entering subloop!
;; |> Entering subloop!
;; => 6 (1 2 2 3 3 4)
```
### Pattern matching
For clauses which bind "body bindings" (every one except (in ...)) can use pattern matching based on Alex Shinn's excellent match.scm.
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.
``` scheme
(loop ((:for (key . val) (in-list '((a . 1) (b . 2) c . 3)))
@ -221,16 +226,32 @@ I also provide simplified forms for many common operations. Omitting :for is all
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:
``` scheme
,expand (loop ((:for a (in-list '(1 2 3 4)))
(:when (even? a))
(:acc acc (listing a))))
$0 = (let* ((final-fun
(lambda (acc) ((@@ (goof) values) acc)))
(tmp-kons (@@ (goof) cons)))
(let loop ((cursor-1 '()) (cursor '(1 2 3 4)))
(if ((@@ (goof) not) ((@@ (goof) pair?) cursor))
(final-fun ((@@ (goof) reverse) cursor-1))
(let ((a ((@@ (goof) car) cursor))
(succ ((@@ (goof) cdr) cursor)))
(if (even? a)
(let ((cursor (tmp-kons a cursor-1)))
(if #f #f)
(loop cursor succ))
(loop cursor-1 succ))))))
;; This is mostly fluff that is removed using DCE, unrolling and inlining:
> ,opt (loop ((:for a (in-list '(1 2 3 4)))
(:when (even? a))
(:acc acc (listing a))))
$1 = (let loopy-loop ((cursor-1 '()) (cursor '(1 2 3 4)))
(if (pair? cursor)
(let ((a (car cursor)) (succ (cdr cursor)))
(if (even? a)
(loopy-loop (cons a cursor-1) succ)
(loopy-loop cursor-1 succ)))
(reverse cursor-1)))
$1 = (let* ((cursor (list 2)) (cursor (cons 4 cursor)))
((@@ (goof) reverse) cursor))
;; well dang, the loop was optimized away almost completely...
;; loop/list, being less general, produces faster code that can be more easily optimized
> ,opt (loop/list ((a (in-list '(1 2 3 4)))
@ -244,7 +265,7 @@ $2 = (list 2 4)
a)
;; This is actually the preferred way to do it in guile. Guile re-sizes the stack, so no stack overflows
$5 = (let loopy-loop ((cursor (read)))
$3 = (let loopy-loop ((cursor (read)))
(if (pair? cursor)
(let ((a (car cursor)) (succ (cdr cursor)))
(if (even? a)
@ -284,6 +305,29 @@ Tests!
Finish documentation.
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.
## foof, what a guy
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.