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
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

View file

@ -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))))

View file

@ -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)))
()
()
()