@ianwlarson has joined the channel
Hello, is there any way to use the return value from a yield statement?
I’m trying to make a generator that can change its state when passed values, but doesn’t necessarily need to be passed values
@ianwlarson the docs for yield show an example of using the return value from a yield expression. is there something else you’re asking about? http://docs.racket-lang.org/reference/Generators.html#%28def._%28%28lib._racket%2Fgenerator..rkt%29._yield%29%29
The problem is it that it demands you use the value
ah, you might want to look at call-with-values.
if you try to NOT use a value, it has arity error
the issue here is the same as the error produced by this code: (define x (values 1 2))
essentially, yield returns a varying number of values depend on the number of arguments passed to the generator. call-with-values can handle an arbitrary number of values, whereas define/define-values/let/let-values only handle a statically-known number of values.
you can, for example, write (call-with-values (lambda () (yield 2)) list), and the values passed to the generator will be collected into a list.
I feel like I need a phd to use this language
there’s a values->list form provided by various utility libraries, perhaps that would be more palatable?
call with values works pretty well
because then I don’t have to unpack the list
I can use a case-lambda with ([() ()’] [(x) ( do something with the value)])
I’m probably misusing this language
Too used to procedural languages
I think it would be cool if yield returned void when nothing is passed to the generator
that would make the zero-args case impossible to distinguish from passing a single argument, which is #<void>.
and it also wouldn’t help at all distinguish between other numbers of arguments.
I think a more pleasant interface would probably be to just produce a list, not multiple values, but some people find multi-valued return “elegant”. I will not comment on that. :)
Does having a let in a named loop have some undesirable interaction over define?
Does the 2nd let cause massive memory growth? If a define was used instead, would it be different?
@ianwlarson after expansion, there is no difference between internal definitions and an equivalent letrec. in that code, loop is called in tail position, so there is no memory cost to the recursion.
so its like
internally converted to a let* or a letrec
not converted but in the actual implementation it’s equivalent
no, it actually is converted; my phrasing was a little misleading. see the grammar for fully-expanded programs: http://docs.racket-lang.org/reference/syntax-model.html#%28part._fully-expanded%29
most notably, define-values and define-syntaxes are only general-top-level-forms. neither appears in the grammar for expr.
I feel like I need a PHD to use this language
to be fair, if you ask a low-level question, you will get a low-level answer. :)
and I’m thankful for it
I just can’t understand it
well, “non-top-level definitions get turned into letrec internally” is probably a good enough, simpler explanation.
“A reference to a local binding in a fully expanded program has a scope set that matches its binding identifier exactly. ”
this was a line in the thing you linked me
which I believe answers what I asked
:sweat_smile: