Fixed named update and a bug in accumulating

* goof.scm: Removed positional updates and fixed named updates
 * iterators.scm: Fixed bug in accumulating where only lists were supported.
This commit is contained in:
Linus 2020-11-04 11:58:58 +01:00
parent b3efccb4aa
commit f2496604d5
3 changed files with 55 additions and 61 deletions

View file

@ -1,14 +1,14 @@
# goof-loop - a scheme looping facility # 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)) (loop ((:for a (in 0 b))
(:for b (in 1 (+ a b))) (:for b (in 1 (+ a b)))
(:for count (up-from 0 (to 1000))) (:for count (up-from 0 (to 1000)))
(:for acc (listing b))) (:acc acc (listing b)))
=> acc => acc
(display b) (newline)) (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)) (loop ((:for a (in-list lst))
(:when (pair? a)) (:when (pair? a))
(:for b (in-list a)) (:for b (in-list a))
(:for acc (summing b))) (:acc acc (summing b)))
=> acc) => 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))) (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 ### 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) ;; => (-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 ;; Shamelessly stolen from Taylor Campbell's foof-loop documentation
(loop continue ((:for element (in-list list)) (define (partition list predicate)
(:acc satisfied (in '())) (loop continue ((:for element (in-list list))
(:acc unsatisfied (in '()))) (:acc satisfied (in '()))
=> (values (reverse satisfied) (:acc unsatisfied (in '())))
(reverse unsatisfied)) => (values (reverse satisfied)
(if (predicate element) (reverse unsatisfied))
(continue (=> satisfied (cons element satisfied))) (if (predicate element)
(continue (=> unsatisfied (cons element unsatisfied)))))) (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? 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 ## foof, what a guy

View file

@ -37,12 +37,16 @@
;; * Adds :when, :unless, and :break clauses that controls when the loop ;; * Adds :when, :unless, and :break clauses that controls when the loop
;; body executes and when values are collected by accumulating for clauses. ;; body executes and when values are collected by accumulating for clauses.
;; similar to how #:when, #:unless and #:break works in racket. ;; 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: ;; It restricts chibi loops in the following ways:
;; * with- and for-clauses are no longer visible in the final expression, for that you ;; * 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) (use-modules (helpers)
@ -51,15 +55,16 @@
(include "iterators.scm") (include "iterators.scm")
;; TODO: Add intermediate subloops. Make sure that accumulators are properly propagated. ;; 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) ;; DONE: fix let-kw-form. Don't use mutation. This should be tested:
;; (loop continue ((:for element (in-list list)) ;; (define (partition predicate list)
;; (:acc satisfied (in '())) ;; (loop continue ((:for element (in-list list))
;; (:acc unsatisfied (in '()))) ;; (:acc satisfied (in '()))
;; => (values (reverse satisfied) ;; (:acc unsatisfied (in '())))
;; (reverse unsatisfied)) ;; => (values (reverse satisfied)
;; (if (predicate element) ;; (reverse unsatisfied))
;; (continue (=> satisfied (cons element satisfied))) ;; (if (predicate element)
;; (continue (=> unsatisfied (cons element unsatisfied)))))) ;; (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) ((_ 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)) (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) ((_ 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 ;; user-whens
((_ orig name l a v c r f ul ((cur-uw ...) . uw-rest) ub ((:when test) clauses ...) . body) ((_ 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) (define (syntax= s1 s2)
(equal? (syntax->datum s1) (syntax->datum s2))) (equal? (syntax->datum s1) (syntax->datum s2)))
(define (named-update? syn) (define (update-name params name val)
(syntax-case syn (=>) (cond
((=> var update) #t) ((null? params) (error "unknown loop parameter name " name (list '=> name val)))
(_ #f))) ((syntax= name (caar params))
(cons (list (caar params) val) (cdr params)))
(else
(define (update-psn! params psn val) (cons (car params) (update-name (cdr params) name 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 (syntax->list stx) (define (syntax->list stx)
(syntax-case stx () (syntax-case stx ()
@ -396,23 +391,16 @@
((_ macro-name (loop-name (var step) ...) . body) ((_ macro-name (loop-name (var step) ...) . body)
(let-syntax ((macro-name (let-syntax ((macro-name
(lambda (stx) (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 ::: (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) (if (null? lst)
(with-syntax ((((v s) :::) params)) (with-syntax ((((v s) :::) params))
#'(loop-name s :::)) #'(loop-name s :::))
(syntax-case (car lst) (=>) (syntax-case (car lst) (=>)
((=> name val) ((=> name val)
(update-name! params #'name #'val) (loop (cdr lst) (update-name params #'name #'val)))
(loop (cdr lst) #f)) (_ (error "Malformed looping clause in macro")))))))))
(val pos
(begin
(update-psn! params psn #'val)
(loop (cdr lst) (+ pos 1))))
(_ (error "Positional arguments cannot be updated after a named argument")))))))))
. body)))) . body))))

View file

@ -268,7 +268,7 @@
(accumulating (kons final i) ((var cursor) x) n . rest)) (accumulating (kons final i) ((var cursor) x) n . rest))
((accumulating (kons final init) ((var cursor) (expr (if check))) n . rest) ((accumulating (kons final init) ((var cursor) (expr (if check))) n . rest)
(n ((tmp-kons kons)) (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)) . rest))
((accumulating (kons final init) ((var cursor) (expr)) n . rest) ((accumulating (kons final init) ((var cursor) (expr)) n . rest)
(n ((tmp-kons kons)) (n ((tmp-kons kons))
((cursor '() (tmp-kons expr cursor))) ((cursor init (tmp-kons expr cursor)))
() ()
() ()
() ()