From 90c2c6bfdfe4560a86b1990c07320ca8fa12fb16 Mon Sep 17 00:00:00 2001 From: Linus Date: Thu, 11 Mar 2021 14:42:52 +0100 Subject: [PATCH] New documentation Added a "Porting" section. Some clarifications. --- documentation/doc.html | 23 +++++++++++++++++++++-- documentation/doc.xml | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/documentation/doc.html b/documentation/doc.html index 5fc52c8..c66b3a2 100644 --- a/documentation/doc.html +++ b/documentation/doc.html @@ -73,7 +73,7 @@ ;; => ((1 . 0) (2 . 0) (2 . 1) (3 . 0))

Loop variables

Both accumulating clauses and :for clauses have something called loop variables. In the case of (:for elt (in-list lst)) the loop variable would be the current pair where elt is the car. Some :acc- or :for-clauses may expose their loop variables so that they can be queried or even updated.

-

In the case of the menioned in-list we can choose to expose the current pair, as in the following example:

+

In the case of the menioned in-list we can choo se to expose the name of the current pair, as in the following example:

 (define (interpose lst between)
   (loop lp ((:for elt pair (in-list lst)))
@@ -85,6 +85,7 @@
 (interpose '(1 2 3 4) ':)        
 ; => (1 : 2 : 3 : 4)        
       

In the above example we chose to bind the loop variable of in-list to pair. Using pair we can then query if the next iteration is the empty list and decide not to interpose any value.

+

Some loop clauses have the option of exposing their loop variable(s). The option to do so is documented under the documentation for each clause.

Simple forms

The pure loop macro is quite a big hammer for most tasks. Often we want to do simple things, like collect elements into a list or a vector, which means the extra housekeeping of separating accumulators and for clauses are too much heavy lifting. goof-loop provides several simpler forms that can be used in those cases. In these simpler forms :acc is disallowed, and everything not identified as anything else is assumed to be a :for clause. The loop below accumulates the 100 first fibonacci numbers into a list.

 (loop/list ((count (up-from 0 (to 100)))
@@ -265,4 +266,22 @@
 
 
 In case of subloops, those are placed instead of <loop-body>. They use the same final-function, and instead of quitting when any <check> triggers they go out to the outer loop.
-    
\ No newline at end of file +

Porting

The bulk of goof-loop is written in portable syntax-rules. That code can be found in goof-impl.scm and all files under the goof directory. The major non-portable part is the macro that is bound in every loop to the user-given name of the loop. In the guile implementation this is implemented in syntax-case, and should be portable to any r6rs scheme. The guile implementation does a non-hygienic comparison of the variables in the named update, so to not have to deal with unwanted shadowing:

+
+(loop lp ((:for a (up-from 0 10)))
+  => '()
+  (let ((a (+ a 1)))
+    (if (odd? a)
+        (cons a (lp (=> a (+ a 2))))
+        (lp))))
+;; => (1 5 9)        
+    

This is just a matter of comfort, since this kind of DWIM has no negative impact. I would regard an implementation without this as conforming as well.

+

The other part is loop/list/parallel. This is not required to actually be parallel, so defining it as an alias to loop/list is acceptable.

+

The third part is also a comfort macro; valid-clause?. It simply verifies that the loop clause is bound and registered as a loop clasue. It makes it possible to get good error messages when a loop clause is misspelled or not imported. It can be portably defined as:

+
+(define-syntax valid-clause?
+  (syntax-rules ()
+    ((_ rest ... ) (rest ...))))
+    

at the expense of error messages that become, for lack of a better word, ultra-shit. The register-loop-clause form can be implemented as whatever NOP you prefer if you chose the above definition. If not, it takes two arguments, type (a symbol of either ’for or ’acc) and the appropriate object. In the guile version this is a syntax object, which is used because it allows the user to import the clause under a different name.

+

If your scheme of choice lacks these kinds of macrobatics, a simple symbol list helps a little bit, and is regarded as conformant.

+
\ No newline at end of file diff --git a/documentation/doc.xml b/documentation/doc.xml index 013d23b..48f2a8b 100644 --- a/documentation/doc.xml +++ b/documentation/doc.xml @@ -116,7 +116,7 @@ Both accumulating clauses and :for clauses have something called loop variables. In the case of `(:for elt (in-list lst))` the loop variable would be the current pair where `elt` is the car. Some :acc- or :for-clauses may expose their loop variables so that they can be queried or even updated. - In the case of the menioned `in-list` we can choose to expose the current pair, as in the following example: + In the case of the menioned `in-list` we can choo se to expose the name of the current pair, as in the following example: (define (interpose lst between) @@ -131,6 +131,8 @@ In the above example we chose to bind the loop variable of `in-list` to `pair`. Using `pair` we can then query if the next iteration is the empty list and decide not to interpose any value. + + Some loop clauses have the option of exposing their loop variable(s). The option to do so is documented under the documentation for each clause. @@ -545,4 +547,33 @@ In case of subloops, those are placed instead of <loop-body>. They use the same final-function, and instead of quitting when any <check> triggers they go out to the outer loop. +
+ The bulk of goof-loop is written in portable syntax-rules. That code can be found in `goof-impl.scm` and all files under the `goof` directory. The major non-portable part is the macro that is bound in every loop to the user-given name of the loop. In the guile implementation this is implemented in syntax-case, and should be portable to any r6rs scheme. The guile implementation does a non-hygienic comparison of the variables in the named update, so to not have to deal with unwanted shadowing: + + + (loop lp ((:for a (up-from 0 10))) + => '() + (let ((a (+ a 1))) + (if (odd? a) + (cons a (lp (=> a (+ a 2)))) + (lp)))) + ;; => (1 5 9) + + + This is just a matter of comfort, since this kind of DWIM has no negative impact. I would regard an implementation without this as conforming as well. + + The other part is loop/list/parallel. This is not required to actually be parallel, so defining it as an alias to loop/list is acceptable. + + The third part is also a comfort macro; `valid-clause?`. It simply verifies that the loop clause is bound and registered as a loop clasue. It makes it possible to get good error messages when a loop clause is misspelled or not imported. It can be portably defined as: + + + (define-syntax valid-clause? + (syntax-rules () + ((_ rest ... ) (rest ...)))) + + + at the expense of error messages that become, for lack of a better word, ultra-shit. The `register-loop-clause` form can be implemented as whatever NOP you prefer if you chose the above definition. If not, it takes two arguments, type (a symbol of either 'for or 'acc) and the appropriate object. In the guile version this is a syntax object, which is used because it allows the user to import the clause under a different name. + + If your scheme of choice lacks these kinds of macrobatics, a simple symbol list helps a little bit, and is regarded as conformant. +