[plt-scheme] suggestions on generators.ss

From: Ryan Culpepper (ryan_sml at yahoo.com)
Date: Sat Apr 8 17:50:50 EDT 2006

--- Danny Yoo <dyoo at hkn.eecs.berkeley.edu> wrote:

> Hi everyone,
> 
> I've been fiddling around a little more with the generator example
> from
> earlier this week, and have revised it to this:
> 
>     http://hkn.eecs.berkeley.edu/~dyoo/tmp/generator.ss
> 
> I was planning to submit this to PLaneT in a bit, as soon as I
> wrote some
> more test cases.  Is there other functionality that people would
> like with
> these things, or any other suggestions?
> 
> For example, I added some non-hygienic syntactic sugar:
> 
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> > (require "generator.ss")
> > (define-generator (evens n)
>     (let loop ((n n))
>       (yield n)
>       (loop (+ n 2))))
> > (let ((my-generator (evens 10)))
>     (list (my-generator) (my-generator) (my-generator)))
> (10 12 14)
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> 
> 
> I know bad things will probably happen if I lexically bind yield:
> 
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> > (define-generator (evens n)
>     (let yield ((n n))
>       (yield n)
>       (yield (+ n 2))))
> > (define f (evens 0))
> > (f)
> ;; hilarity ensues
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> 
> I think a solution would be to somehow restrict 'yield as a real
> keyword
> in the context of a define-generator (though I'm not quite sure how
> to do
> this yet).  But perhaps the non-hygienic macro is just a bad idea?

Another way you can do this that avoids the problems Dave mentioned
is to use a "syntax-parameter". It's like a parameter, but for
macros. Here's how:

Consider a related problem: what if we wanted "yield" to work in the
*dynamic extent* of a call to a generator, rather than just lexically
in the generator's body? Here's how you'd do it:

  (define current-yielder
    (make-parameter 
      (lambda (value) (error 'yield "not in a generator"))))
  (define (yield value) ((current-yielder) value)

Then define-dynamic-generator wraps the generator function with
something that sets the current-yielder parameter.

  (define-syntax define-dynamic-generator
    (syntax-rules ()
      [(define-generator (name . args) . body)
       (define (name . args)
         (let ([name
                (lambda (yielder)
                  (parameterize ([current-yielder yielder])
                    . body))])
           (make-generator name)))]))

(As an aside, this version passes your test suite, too.)

Using parameters, you can change an issue of *binding* into an issue
of *common reference* to a side-channel of communication like a
parameter or a box.

Syntax parameters work the same way. You agree on a keyword:

  (require (lib "stxparam.ss"))

  (define-syntax-parameter yield
    (lambda (stx)
      (raise-syntax-error #f "used outside of a generator" stx)))

Then you when you want some code to be able to use yield, like the
body of your generator function, you use "syntax-parameterize":

  (define-syntax define-generator
    (syntax-rules ()
      [(define-generator (name . args) . body)
       (define (name . args)
         (let ([name 
                (lambda (yielder)
                  (syntax-parameterize 
                       ([yield
                         (syntax-rules ()
                           [(yield value)
                            (yielder value)])])
                    . body))])
           (make-generator name)))]))

This use of "syntax-parameterize" changes the meaning of "yield" in
the body to just call the supplied "yielder" function. If you want to
be able to use "yield" as an expression by itself, rather than
requiring it to be called, you can use syntax-id-rules to define an
identifier macro instead.

By using "syntax-parameterize", define-generator doesn't *bind*
"yield"; it *refers to* "yield". Any macro that expands into a
generator definition will still be producing a reference to "yield",
and references don't suffer the same interactions with hygiene that
bindings do.

Ryan



Posted on the users mailing list.