[plt-scheme] srfi-42, values, named let, etc.
Hi David,
> Is there some way of modifying it so that it plays nice with
> :parallel? Alternatively, is there some way of modifying :parallel so
> that it plays nice with the above ?
Tough questions. My first thought is that the problem might be
rooted in the definition of :parallel :
(:parallel <generator>*)
Runs several generators in parallel. This means that the next
binding in the sequence is obtained by advancing each generator in
<generator>* by one step. The parallel generator terminates when any
of its component generators terminates. The generators share a common
scope for the variables they introduce. This implies that the names of
the variables introduced by the various generators must be distinct.
The part that troubles me is the: "The generators share a common
scope for the variables they introduce."
If I recall correctly, my first implementation gave each generator
its own scope. Whether it was accident or whether I just thought
it was the most natural, I don't remember.
In fact the following test exposes whether or not the introduced
variables share scope
(my-check
(list-ec (:parallel (:integers x)
(:do ((i 10)) (< x i) ((- i 1))))
(list x i))
=> '((0 10) (1 9) (2 8) (3 7) (4 6)) )
This relates to your G) which asked why the following
expressions didn't return the same result:
(let ([a 1][b 1])
(zip
(list-ec (:do ((a 0)) (< a 10) ((+ a b))) a)
(list-ec (:do ((b 0)) (< b 10) ((+ a b))) b)))
(let ([a 1][b 1])
(list-ec (:parallel
(:do ((a 0)) (< a 10) ((+ a b)))
(:do ((b 0)) (< b 10) ((+ a b)))) (list a b)))
[NB: The last expressions crashes DrScheme, if no memory limit is set!]
It seems to me that both types of :parallel (with and without shared
scope of introduced variables) are useful. If normal naming were used,
they ought to be called :parallel (separate scope) and :parallel-rec
(shared scope), but since the one with shared scope is named :parallel
in SRFI42, we need another name. In lieu of a better name I'll
call it :parallel-sep.
The definition of :parallel is given by the following - the
interesting clause is the one with two generators:
(define-generator (:parallel form-stx)
; TODO: Check that all subforms are generators
(syntax-case form-stx (index)
[(_ (index i) q ...)
(add-index form-stx #'(_ q ...) #'i)]
[(_ gen)
(generator->loop #'gen)]
[(_ gen1 gen2)
(syntax-case (list (loop-stx (generator->loop #'gen1))
(loop-stx (generator->loop #'gen2))) ()
[(((ob ...) (oc ...) (lb ...) ne1
(ib ...) (ic ...) ne2 (ls ...))
((ob2 ...) (oc2 ...) (lb2 ...) ne12
(ib2 ...) (ic2 ...) ne22 (ls2 ...)))
(make-loop
#'((ob ... ob2 ...)
(oc ... oc2 ...) (lb ... lb2 ...)
(and ne1 ne12) (ib ... ib2 ...)
(ic ... ic2 ...) (and ne2 ne22)
(ls ... ls2 ...)))])]
[(_ gen1 gen2 gen3 ...)
#'(:parallel (:parallel gen1 gen2) gen3 ...)]))
As can be seen the outer bindings (ob ...) and (ob2 ...)
are merged into (ob ... ob2 ...) which means that get to
share scope.
To define a :parallel-sep we can introduce a marker,
and use it to mark everything that stem from the
second generator. One way to introduce a new mark
is to use make-syntax-introducer to make a syntax-marker
with a fresh mark.
Are you interested in giving it a go?
--
Jens Axel Søgaard