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

From: Greg Hendershott (greghendershott at gmail.com)
Date: Fri Sep 14 09:55:00 EDT 2012

> I thought I'd also tried `in-producer' without
> success, but I may have made a mistake.

I had difficulty with the `stop' arg to `in-producer'.  It can't
simply be void or void?.

I looped back to try this again, and finally figured it out (although
maybe the following isn't the most concise way to put it?):

(define (in-gen-multi)
  (define (stop? . xs)
    (match xs
      [(list 'end 'end) #t]
      [else #f]))
  (in-producer (generator ()
                          (for ([i (in-range 4)])
                            (yield i 0))
                          (yield 'end 'end))
               stop?))
(for/list ([(x y) (in-gen)])
  (cons x y))

OK, so now I'm even interested in my question about generator
performance. Because it would be elegant to express what I want using
a generator.

On Fri, Sep 14, 2012 at 9:14 AM, Greg Hendershott
<greghendershott at gmail.com> wrote:
> Thank you Matthew. I thought I'd also tried `in-producer' without
> success, but I may have made a mistake.
>
> Since I posted, I realized I don't need to use a generator at all, so
> I'm not road-blocked.
>
> Speaking of which, I'm curious what are folks' attitudes toward
> generators? I ask because I rarely see them used in Racket code I've
> perused. Is that just because generators are relatively new? Or is it
> because they are convenient but somewhat "heavy" performance-wise?
>
> On Fri, Sep 14, 2012 at 8:53 AM, Matthew Flatt <mflatt at cs.utah.edu> wrote:
>> 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.