Changed accumulator test/bind order to bind/test
This means vectoring exits directly when the index loop variable = :length. It also means :final has to change.
This commit is contained in:
		
							parent
							
								
									aa77fef2ad
								
							
						
					
					
						commit
						7a1137e579
					
				
					 3 changed files with 71 additions and 66 deletions
				
			
		
							
								
								
									
										121
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										121
									
								
								README.md
									
										
									
									
									
								
							|  | @ -4,75 +4,29 @@ goof-loops aims to be an amalgamation of the racket for loops and Alex Shinn's ( | ||||||
| 
 | 
 | ||||||
| Compared to foof-loop, some things are added. Apart from minor syntactic changes, subloops are supported. The best way is to show: | Compared to foof-loop, some things are added. Apart from minor syntactic changes, subloops are supported. The best way is to show: | ||||||
| 
 | 
 | ||||||
| ``` | ``` scheme | ||||||
| (define lst '((1 2) dud (3 4) (5 6))) | (define lst '((1 2) dud (3 4) (5 6))) | ||||||
| (loop ((:for a (in-list lst)) | (loop ((:for a (in-list lst)) | ||||||
|        (:when (pair? a)) |        (:when (pair? a)) | ||||||
|        (:for b (in-list a)) |        (:for b (in-list a)) | ||||||
|        (:acc acc (summing b))) |        (:acc acc (summing b))) | ||||||
|   => acc) |   => acc) | ||||||
|  | ;; => 21 | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| 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. |  Any :when, :unless, :break, :final, :bind, :do or :subloop clause will break out a subloop if any subsequent for clauses are found. | ||||||
| 
 | 
 | ||||||
| ## Beta warning | ## Beta warning | ||||||
| 
 | 
 | ||||||
| This is beta quality software, and some minor details are likely to change. I have gotten most kinks worked out though. | 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. | ||||||
| 
 | 
 | ||||||
| ## Documentation | ## Documentation | ||||||
| 
 | 
 | ||||||
| The current WIP documentation can be found here: https://bjoli.srht.site/doc.html | The current WIP documentation can be found here: https://bjoli.srht.site/doc.html (WARNING: for 0.1, not master) | ||||||
| 
 | 
 | ||||||
| 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. | 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. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ## 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. |  | ||||||
| 
 |  | ||||||
| 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]])) in case of loop clauses, or (:acc var (folding init [step])) in case of accumulators. |  | ||||||
| 
 |  | ||||||
| ### Higher order loop protocol |  | ||||||
| 
 |  | ||||||
| goof supports a higher order looping protocol, based on srfi-158 generators: |  | ||||||
| 
 |  | ||||||
|     (loop ((:for food (in-list '(banana cake grape cake bean cake))) |  | ||||||
|            (:for true? (in-cycle (in-list '(#t #f))))) |  | ||||||
|        (display "The ")  |  | ||||||
|        (display food) |  | ||||||
|        (display " is a ") |  | ||||||
|        (if true? |  | ||||||
|           (display food) |  | ||||||
|           (display "LIE!")) |  | ||||||
|       (newline)) |  | ||||||
| 
 |  | ||||||
| In the above example true? never ends, but restarts every time the list is exhausted.  |  | ||||||
| 
 |  | ||||||
| ### 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).  |  | ||||||
| 
 |  | ||||||
| ### 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))) |  | ||||||
| 
 |  | ||||||
| ## Features | ## Features | ||||||
| 
 | 
 | ||||||
| ### Lexical order of clauses | ### Lexical order of clauses | ||||||
|  | @ -87,6 +41,18 @@ Due to clause reordering, positional updates are not supported. If you want to u | ||||||
|   => acc) |   => acc) | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | 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. | ||||||
|  | 
 | ||||||
|  | ``` scheme | ||||||
|  | (loop ((:for a (in-list '(1 2 3))) | ||||||
|  |        (:acc vec (vectoring a (:length 2))) | ||||||
|  |        ;; implicit :break (= vec-index 2) | ||||||
|  |        (:acc sum (summing a))) | ||||||
|  |   => (values vec sum)) | ||||||
|  | ;; => #(1 2) 1 | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
| ### Loop naming to make it "fold right" | ### Loop naming to make it "fold right" | ||||||
| 
 | 
 | ||||||
| You can of course still have a larger control of when to loop by naming your loop: | You can of course still have a larger control of when to loop by naming your loop: | ||||||
|  | @ -131,6 +97,26 @@ The iterator protocol allows exposing the loop variables | ||||||
|        |        | ||||||
| ;; => (1 : 2 : 3) | ;; => (1 : 2 : 3) | ||||||
| ``` | ``` | ||||||
|  | In the above example, pair is bound to the pair where elt is the car. | ||||||
|  | 
 | ||||||
|  | ### Higher order loop protocol | ||||||
|  | 
 | ||||||
|  | goof supports a higher order looping protocol, based on srfi-158 generators: | ||||||
|  | 
 | ||||||
|  | ``` scheme | ||||||
|  | 
 | ||||||
|  | (loop ((:for food (in-list '(banana cake grape cake bean cake))) | ||||||
|  |        (:for true? (in-cycle (in-list '(#t #f))))) | ||||||
|  |   (display "The ")  | ||||||
|  |   (display food) | ||||||
|  |   (display " is a ") | ||||||
|  |   (if true? | ||||||
|  |      (display food) | ||||||
|  |      (display "LIE!")) | ||||||
|  |   (newline)) | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | In the above example true? never ends, but restarts every time the list is exhausted.  | ||||||
| 
 | 
 | ||||||
| ### :final is context sensitive (compared to Racket's #:final) | ### :final is context sensitive (compared to Racket's #:final) | ||||||
| 
 | 
 | ||||||
|  | @ -145,9 +131,9 @@ The iterator protocol allows exposing the loop variables | ||||||
| ;; => ((1 . a) (1 . b) (2 . a) (2 . b)) | ;; => ((1 . a) (1 . b) (2 . a) (2 . b)) | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| The racket counterpart would result in ((1 . a) (1 . b) (2 . a)) | The racket counterpart would result in ((1 . a) (1 . b) (2 . a)). This comes at :final clauses being less efficient than racket's #:final, but not by much.  | ||||||
| 
 | 
 | ||||||
| ### for-clauses can refer to eachother | ### :for-clauses can refer to eachother | ||||||
| 
 | 
 | ||||||
| The iterative fibonacci loop is weird to write using for/fold. goof fixes this: | The iterative fibonacci loop is weird to write using for/fold. goof fixes this: | ||||||
| ``` scheme | ``` scheme | ||||||
|  | @ -171,7 +157,18 @@ The iterative fibonacci loop is weird to write using for/fold. goof fixes this: | ||||||
| ;; => 6 (1 2 2 3 3 4) | ;; => 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. | ||||||
|  | 
 | ||||||
|  | ``` scheme | ||||||
|  | (loop ((:for (key . val) (in-list '((a . 1) (b . 2) c . 3))) | ||||||
|  |        (:acc sum (summing val))) | ||||||
|  |   => sum) | ||||||
|  | ;; => 6 | ||||||
|  | 
 | ||||||
|  | This also works with :bind clauses. | ||||||
|  | ``` | ||||||
| 
 | 
 | ||||||
| ### Simple forms | ### Simple forms | ||||||
| I also provide simplified forms for many common operations. Omitting :for is allowed, and :acc clauses are not allowed. | I also provide simplified forms for many common operations. Omitting :for is allowed, and :acc clauses are not allowed. | ||||||
|  | @ -245,7 +242,7 @@ $5 = (let loopy-loop ((cursor (read))) | ||||||
|     (let ((a (car cursor)) (succ (cdr cursor))) |     (let ((a (car cursor)) (succ (cdr cursor))) | ||||||
|       (if (even? a) |       (if (even? a) | ||||||
|         (cons a (loopy-loop succ)) |         (cons a (loopy-loop succ)) | ||||||
|         (loopy-loop |         (loopy-loop))))) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ;; The code expansion of the partition procedure above produces | ;; The code expansion of the partition procedure above produces | ||||||
|  | @ -265,15 +262,23 @@ $5 = (let loopy-loop ((cursor (read))) | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | ## Differences from foof-loop | ||||||
|  | 
 | ||||||
|  | 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).  | ||||||
|  | 
 | ||||||
| ## Todo | ## Todo | ||||||
| Tests! | Tests! | ||||||
| 
 | 
 | ||||||
| Finish documentation. | Finish documentation. | ||||||
| 
 | 
 | ||||||
| ## foof, what a guy | ## 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. | ||||||
| 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 | ## Licence | ||||||
| 
 | 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. | ||||||
| The same BSD-styled license Alex uses for chibi-loop. |  | ||||||
|  |  | ||||||
|  | @ -54,6 +54,7 @@ | ||||||
|     ((loop . rest) |     ((loop . rest) | ||||||
|      (%loop (loop . rest) . rest)))) |      (%loop (loop . rest) . rest)))) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| (define-syntax %loop | (define-syntax %loop | ||||||
|   (syntax-rules (=>) |   (syntax-rules (=>) | ||||||
|     ((%loop o () => expr body ...) |     ((%loop o () => expr body ...) | ||||||
|  | @ -142,9 +143,8 @@ | ||||||
|     ((_ orig name l a v c r f ff ((cur-ub ...) . ub-rest)  ((:break expr) clauses ...) . body) |     ((_ orig name l a v c r f ff ((cur-ub ...) . ub-rest)  ((:break expr) clauses ...) . body) | ||||||
|      (cl orig name l a v c r f ff ((cur-ub ... (:break expr)) . ub-rest) (clauses ...) . body)) |      (cl orig name l a v c r f ff ((cur-ub ... (:break expr)) . ub-rest) (clauses ...) . body)) | ||||||
|     ;; user final |     ;; user final | ||||||
|     ;; This pushes a #t to the user when expression, thus forcing a subloop if a for-clause is found afterwards. |     ((_ orig name l a v c r f ff ((cur-user ...) . user-rest) ((:final expr) clauses ...) . body) | ||||||
|     ((_ orig name l a v c r f ff user ((:final expr) clauses ...) . body) |      (final :acc ((fin) (expr)) cl-next/acc orig name l a v c r f ff ((cur-user ... (:break fin)) . user-rest) (clauses ...) . body)) | ||||||
|      (final :acc ((_) (expr)) cl-next/acc orig name l a v c r f ff user (clauses ...) . body)) |  | ||||||
| 
 | 
 | ||||||
|     ;; User do - sideffecting stuff. |     ;; User do - sideffecting stuff. | ||||||
|     ((_ orig name l a v c r f ff ((cur-uw ...) . uw-rest) ((:do expr ...) clauses ...) . body) |     ((_ orig name l a v c r f ff ((cur-uw ...) . uw-rest) ((:do expr ...) clauses ...) . body) | ||||||
|  | @ -196,7 +196,7 @@ | ||||||
|          checks |          checks | ||||||
|          ((refs ... new-refs ...)) |          ((refs ... new-refs ...)) | ||||||
|          (finals ... new-finals ...) |          (finals ... new-finals ...) | ||||||
|          ff ((cur-ub ... (:break  new-checks) ... (:bind (accvar accupdate) ...)) . ub-rest) clauses . body)) |          ff ((cur-ub ... (:bind (accvar accupdate) ...) (:break  new-checks) ... ) . ub-rest) clauses . body)) | ||||||
|     ;; We have ONE subloop! |     ;; We have ONE subloop! | ||||||
|     ((_ (new-lets ...) ((accvar accinit accupdate) ...) (new-checks ...) (new-refs ...) (new-finals ...) |     ((_ (new-lets ...) ((accvar accinit accupdate) ...) (new-checks ...) (new-refs ...) (new-finals ...) | ||||||
|         orig name |         orig name | ||||||
|  |  | ||||||
|  | @ -512,13 +512,13 @@ | ||||||
| ;; this is an internal "accumulator". It is used for final tests | ;; this is an internal "accumulator". It is used for final tests | ||||||
| ;; :final in goof differs from in racket. It is lexical, meaning it | ;; :final in goof differs from in racket. It is lexical, meaning it | ||||||
| ;; is tested where it is placed in the clauses, and any subloop is | ;; is tested where it is placed in the clauses, and any subloop is | ||||||
| ;; executed completely. | ;; executed until exhaustion. | ||||||
| (define-syntax final | (define-syntax final | ||||||
|   (syntax-rules (:acc) |   (syntax-rules (:acc) | ||||||
|     ((_ :acc ((var) (test)) n . rest) |     ((_ :acc ((var) (test)) n . rest) | ||||||
|      (n () |      (n () | ||||||
|         ((final #f test)) |         ((var #f test)) | ||||||
|         (final) |         () | ||||||
|         () |         () | ||||||
|         () |         () | ||||||
|         . rest)))) |         . rest)))) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Linus
						Linus