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 ) 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.
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).
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.
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).
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.