diff --git a/README.md b/README.md index 7f08b97..6aac2bd 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # goof-loop - a scheme looping facility -WARNING: CURRENTLY PRE-ALPHA. The examples in this document are not consistent with the current direction I am pushing this (even though they _should_ work. +WARNING: CURRENTLY PRE-ALPHA. The examples in this document are not consistent with the current direction I am pushing this (even though they _should_ work). -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: +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))) - (:for acc (listing b))) + (:acc acc (listing b))) => acc (display b) (newline)) ``` @@ -22,7 +22,7 @@ Compared to foof-loop, some things are added. Apart from minor syntactic changes (loop ((:for a (in-list lst)) (:when (pair? a)) (:for b (in-list a)) - (:for acc (summing b))) + (:acc acc (summing b))) => acc) ``` @@ -54,7 +54,7 @@ only :acc clauses are visible in the final-expression. This is due to for-clause (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. +Positional updates of variables is not supported, due to goof-loop reordering the loop-vars - which there are reasons for. ### similarities @@ -70,18 +70,22 @@ You can of course still have a larger control of your loops: ;; => (-1 4 -9 16 -25 36 -49 64 -81 100) ``` -Named updates have a bug, sadly, but works if there is only _one_ instance of the iteration macro. This doesn't curretnly work, but will in a little time: +Named updates also work. ``` ;; Shamelessly stolen from Taylor Campbell's foof-loop documentation -(loop continue ((:for element (in-list list)) - (:acc satisfied (in '())) - (:acc unsatisfied (in '()))) - => (values (reverse satisfied) - (reverse unsatisfied)) - (if (predicate element) - (continue (=> satisfied (cons element satisfied))) - (continue (=> unsatisfied (cons element unsatisfied)))))) +(define (partition list predicate) + (loop continue ((:for element (in-list list)) + (:acc satisfied (in '())) + (:acc unsatisfied (in '()))) + => (values (reverse satisfied) + (reverse unsatisfied)) + (if (predicate element) + (continue (=> satisfied (cons element satisfied))) + (continue (=> unsatisfied (cons element unsatisfied)))))) + +(partition '(1 2 3 4 5) odd?) +;; => (values (1 3 5) (2 4)) ``` @@ -95,7 +99,9 @@ Should we add finalizers for :for-clauses? I can't see the need outside of a pot 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. +Add racket #:final clauses. + +Add simple versions of loop. loop/list, loop/sum, loop/last, loop/first, and so on. ## foof, what a guy diff --git a/goof.scm b/goof.scm index a5f0407..a22b875 100644 --- a/goof.scm +++ b/goof.scm @@ -37,12 +37,16 @@ ;; * Adds :when, :unless, and :break clauses that controls when the loop ;; body executes and when values are collected by accumulating for clauses. ;; similar to how #:when, #:unless and #:break works in racket. -;; * Planned: add support for subloops, akin to what the starified loops of -;; racket do. ;; ;; It restricts chibi loops in the following ways: ;; * with- and for-clauses are no longer visible in the final expression, for that you -;; must use a clause for which I don't have a name yet. +;; must use an accumulator clause. +;; * Positional update is not supported. It seems error-prone once you start +;; having a lot of loop variables, and because goof-loop does some re-ordering +;; that foof loop does not. For example: +;; (:for a (in 0 (+ a 1))) (:acc acc (in '() (cons a acc))) +;; are actually reordered in a loop, because accumulators and for loops are separated +;; due to having to propagate the accumulators through the loop. (use-modules (helpers) @@ -51,15 +55,16 @@ (include "iterators.scm") ;; TODO: Add intermediate subloops. Make sure that accumulators are properly propagated. -;; TODO. fix let-kw-form. Don't use mutation. This should work:(define (partition predicate list) -;; (loop continue ((:for element (in-list list)) -;; (:acc satisfied (in '())) -;; (:acc unsatisfied (in '()))) -;; => (values (reverse satisfied) -;; (reverse unsatisfied)) -;; (if (predicate element) -;; (continue (=> satisfied (cons element satisfied))) -;; (continue (=> unsatisfied (cons element unsatisfied)))))) +;; DONE: fix let-kw-form. Don't use mutation. This should be tested: +;; (define (partition predicate list) +;; (loop continue ((:for element (in-list list)) +;; (:acc satisfied (in '())) +;; (:acc unsatisfied (in '()))) +;; => (values (reverse satisfied) +;; (reverse unsatisfied)) +;; (if (predicate element) +;; (continue (=> satisfied (cons element satisfied))) +;; (continue (=> unsatisfied (cons element unsatisfied)))))) @@ -130,7 +135,10 @@ ((_ orig name l a v c r f ul uw ub ((:acc var (in init update)) clauses ...) . body) (cl-next () ((var init update)) () () () ((var var)) orig name l a v c r f ul uw ub (clauses ...) . body)) ((_ orig name l a v c r f ul uw ub ((:acc var (in init)) clauses ...) . body) - (cl-next () ((var init var)) () () () ((var var)) orig name l a v c r f ul uw ub (clauses ...) . body)) + (cl-next () ((var init var)) () () () ((var var)) orig name l a v c r f ul uw ub (clauses ...) . body)) + ;; Accumulator clause with a proper accumulator. + ((_ orig name l a v c r f ul uw ub ((:acc id ids ... (iterator source ...)) clauses ...) . body) + (iterator ((id ids ...) (source ...)) cl-next orig name l a v c r f ul uw ub (clauses ...) . body)) ;; user-whens ((_ orig name l a v c r f ul ((cur-uw ...) . uw-rest) ub ((:when test) clauses ...) . body) @@ -366,26 +374,13 @@ (define (syntax= s1 s2) (equal? (syntax->datum s1) (syntax->datum s2))) -(define (named-update? syn) - (syntax-case syn (=>) - ((=> var update) #t) - (_ #f))) - - -(define (update-psn! params psn val) - (list-set! params psn - (list (car (list-ref params psn)) val))) - -(define (update-name! params name val) - (let loop ((params params)) - (cond - ((null? params) (error "unknown loop parameter name " name (list '=> name val))) - ((syntax= name (caar params)) - (set-cdr! (car params) (list val)) - (display (syntax->datum val)) - ) - (else - (loop (cdr params)))))) +(define (update-name params name val) + (cond + ((null? params) (error "unknown loop parameter name " name (list '=> name val))) + ((syntax= name (caar params)) + (cons (list (caar params) val) (cdr params))) + (else + (cons (car params) (update-name (cdr params) name val))))) (define (syntax->list stx) (syntax-case stx () @@ -396,23 +391,16 @@ ((_ macro-name (loop-name (var step) ...) . body) (let-syntax ((macro-name (lambda (stx) - ;; this way of formulating params means it is an alist with syntax objects - ;; as keys instead of a list of syntax objects - (define params (list #'(var step) ...)) (with-ellipsis ::: - (let loop ((lst (cdr (syntax->list stx))) (pos 0)) + (let loop ((lst (cdr (syntax->list stx))) + (params (list #'(var step) ...))) (if (null? lst) (with-syntax ((((v s) :::) params)) #'(loop-name s :::)) (syntax-case (car lst) (=>) ((=> name val) - (update-name! params #'name #'val) - (loop (cdr lst) #f)) - (val pos - (begin - (update-psn! params psn #'val) - (loop (cdr lst) (+ pos 1)))) - (_ (error "Positional arguments cannot be updated after a named argument"))))))))) + (loop (cdr lst) (update-name params #'name #'val))) + (_ (error "Malformed looping clause in macro"))))))))) . body)))) diff --git a/iterators.scm b/iterators.scm index c230c47..1a4329e 100644 --- a/iterators.scm +++ b/iterators.scm @@ -268,7 +268,7 @@ (accumulating (kons final i) ((var cursor) x) n . rest)) ((accumulating (kons final init) ((var cursor) (expr (if check))) n . rest) (n ((tmp-kons kons)) - ((cursor '() (if check (tmp-kons expr cursor) cursor))) + ((cursor init (if check (tmp-kons expr cursor) cursor))) () () () @@ -276,7 +276,7 @@ . rest)) ((accumulating (kons final init) ((var cursor) (expr)) n . rest) (n ((tmp-kons kons)) - ((cursor '() (tmp-kons expr cursor))) + ((cursor init (tmp-kons expr cursor))) () () ()