Predicate to check for `(values)`?

If I define a generator

(require racket/generator)

(define a-generator
  (generator ()
    (yield 1)
    (yield 2)))

and call a-generator repeatedly, I get

> (a-generator)
1
> (a-generator)
2
> (a-generator)
> (a-generator)

That is, after 1 and 2 are yielded, nothing is displayed in the REPL. When I try to print the result, I get an arity error, so I suspected that this is a multiple-values thing.

And indeed,

(define-values () (a-generator))

succeeds.

Now I wonder if it would be possible to implement a predicate function to check if a value (or, actually, non-values :wink: ) is (values). This would probably be tricky; I'm not even sure if there's a way to write a function that could be called as (predicate? (values)) in the first place, regardless of a check in the function.

Even "in-line", without defining a function, a check seems to be non-trivial or not possible. I thought it might work with match, but all I found in the match documentation seems to require that the number of values in (values ...) is known beforehand.

1 Like

@soegaard pointed me to call-with-values (see 10.1 Multiple Values), and indeed,

> (call-with-values a-generator list)
'()

works. This doesn't answer how I could call a function with the result of a-generator, but the suggestion is good enough for me. :slight_smile:

1 Like

An arguably more direct way is:

(call-with-values 
 a-generator
 (case-lambda
  [() #t]
  [else #f]))

4 Likes

Racket generators repeatedly return what the body of the generator last returned when it finishes evaluating. In this case, I'd expect a #<void> value like you normally get from an equivalent lambda expression, but (void? (a-generator)) on your exhausted generator gives an arity error. It actually doesn't return anything, which I didn't think was possible.

Anyways, to check to see if a Racket-style generator is finished, use generator-state instead of depending on the return value. Though that won't return 'done until after your generator has returned an empty value...

(I prefer SRFI-158 style generators that return end-of-file when done (Available in my extra-srfi-libs collection). They're usually easier to work with (Unless EOF is a valid return value, in which case, have to drag boxes or something into it).

2 Likes

I like both

(null? (call-with-values a-generator list))

and your approach. The former because it's a simpler expression and yours because it's more explicit.

In this context, this is of course the way to go. I just got sidetracked because of the "funny" return value of the generator. :wink:

That said, In my concrete use case the easiest way was to return #f at the end of the generator and use in-producer.

I was also quite surprised at first and agree that (void? (a-generator)) would be more "natural".

It depends on what you mean by "anything". You might as well argue that a function

(define (test)
  (values))

returns something, namely (values). :wink:

It's "just" a special case of using values:

> (values 1 2)
1
2
> (values 1)  ; same as 1
1
> (values)

Generally, I think if #f or (void) as special values supposedly aren't enough, it's better to define a unique value with (define unique-values (gensym)) instead of "inventing" more obscure values that might or might not conflict with client code.

That said, maybe returning (values) from an exhausted generator is just a side effect of the implementation, not an intentional special value.

1 Like