[racket] Can't `yield' multiple values when `in-generator' is returned from a function?

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Fri Sep 14 08:53:27 EDT 2012

At Fri, 14 Sep 2012 08:20:22 -0400, Greg Hendershott wrote:
> tl'dr: I can `yield' multiple values when an `in-generator' sequence
> is used directly in a comprehension, but not when it is returned from
> a function. I'm stumped.
> 
> 
> Let's try using the `in-generator' sequence directly in a comprehension:
> 
> ;; Yield 1 value, directly using `in-generator`
> (for/list ([x (in-generator (for ([i (in-range 4)])
>                               (yield i)))])
>   x)
> ;; => '(0 1 2 3)
> 
> ;; Yield 2 values, directly using `in-generator`
> (for/list ([(x y) (in-generator (for ([i (in-range 4)])
>                                   (yield i 0)))])
>   (cons x y))
> ;; => '((0 . 0) (1 . 0) (2 . 0) (3 . 0))
> 
> Great!
> 
> Next, let's try using the `in-generator' sequence as returned from a
> function. As best I can understand from the docs, this should work. It
> does for the case of yield-ing 1 value -- but not 2 values:
> 
> ;; Yield 1 value, using `in-generator' returned from a function:
> (define (in-gen-1)
>   (in-generator (for ([i (in-range 4)])
>                   (yield i))))
> (for/list ([x (in-gen-1)])
>   x)
> ;; => '(0 1 2 3)
> 
> ;; Yield 2 values, using `in-generator' returned from a function:
> (define (in-gen-2)
>   (in-generator (for ([i (in-range 4)])
>                                   (yield i 0))))
> (for/list ([(x y) (in-gen-2)])
>   (cons x y))
> ;; ==> EXCEPTION:
> ; .../private/for.rkt:1046:17: arity mismatch;
> ;  the expected number of arguments does not match the given number
> ;   expected: 1
> ;   given: 2
> ;   arguments...:
> ;    0
> ;    0
> 
> Why?

It's a problem in `in-generator' --- specifically in the way that
`in-generator' expands to `in-producer' when the `in-generator'
expression is not in a `for' right-hand side. In that case, the
`in-generator' expansion doesn't know how many "stop" values it's
supposed to produce.


To work around the problem, you can use `in-producer' and `generator':

 ;; Yield 2 values, using `in-generator' returned from a function:
 (define (in-gen-2)
   (in-producer (generator ()
                           (for ([i (in-range 4)])
                             (yield i 0))
                           (values 'done 'done))
                (lambda (a b) (eq? a 'done))))

 (for/list ([(x y) (in-gen-2)])
   (cons x y))


I'll work on a repair for `in-generator'.


Posted on the users mailing list.