[plt-dev] New scheme/generator functionality

From: Eli Barzilay (eli at barzilay.org)
Date: Fri Feb 5 11:49:59 EST 2010

The scheme/generator library has now some extended functionality in
svn, and is included in last night's build:

* `yield' can accept any number of values:

    -> (define g (generator (yield 1 2)))
    -> (g)
    1
    2

* The generator function itself can accept values, which are sent back
  to the generator as the result of the `yield' call:

    -> (define g (generator (printf "Got ~s\n" (yield 1 2)) 3))
    -> (g)
    1
    2
    -> (g 4)
    Got 4
    3
    -> (g)
    3

  This is similar to using g.send() in python -- and in a similar way,
  you get:

    -> (define g (generator (yield 1)))
    ;; at this point, the generator didn't start running yet, so there
    ;; is no `yield' expression waiting for a result:
    -> (g 2)
    generator: cannot send a value to a fresh generator
    -> (g)
    1
    ;; the generator used `yield' to return 1, and it is now
    ;; suspended, waiting for a result of that expression, `g' can be
    ;; used to return such a result (with any number of values):
    -> (g 2)
    2
    ;; so the generator is now terminated -- the last value in its
    ;; body is the result of that `yield' which is the 2 that was sent
    ;; to it -- and that value is now repeated for ever:
    -> (g)
    2
    -> (g)
    2
    ;; this state is similar to the initial state: there is no `yield'
    ;; expression waiting for an answer:
    -> (g 3)
    generator: cannot send a value to a done generator

* This is related to a possibly confusing situation:

    -> (define g (generator (yield 1) (yield 2)))
    -> (list (g) (g))
    (1 2)
    -> (list (g))
    context expected 1 value, received 0 values

  The reason for that error is that the third (g) call provides the
  values that result from the second `yield' expression -- and since
  that expression is the last in the generator's body, those values
  are what gets returned from this point on.  So:

    -> (define g (generator (yield 1) (yield 2)))
    -> (list (g) (g) (g 99))
    (1 2 99)

  Note also the confusing off-by-one differnce: the first (g) starts
  the generator, the second one provides the result for the first
  `yield' etc.

  Here's a toy delay thing:

    -> (define g (generator (let loop ([x 1] [y 2]) (loop y (yield x)))))
    -> (g)
    1
    -> (g 3)
    2
    -> (g 4)
    3
    -> (g 5)
    4

* Finally, since it is useful to know what state the generator is in,
  there is a new `generator-state' function:

    -> (define g (generator (yield 1) (yield 2)))
    -> (generator-state g)
    fresh
    -> (g)
    1
    -> (generator-state g)
    suspended
    -> (g)
    2
    -> (generator-state g)
    suspended
    -> (g)
    -> (generator-state g)
    done

    -> (define g (generator ((yield 1))))
    -> (g)
    1
    -> (g (lambda () (generator-state g)))
    running
    -> (generator-state g)
    done
    -> (g) ; since it's done, it repeats this forever
    running

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!


Posted on the dev mailing list.