
I am trying to understand generators and I can’t quite understand the following:
> (define gen (generator () (map yield '(a b c))))
> (gen)
'a
> (gen)
; result arity mismatch;
; expected number of values not received
; expected: 1
; received: 0
; Context (plain; to see better errortrace context, re-run with C-u prefix):
; .../private/map.rkt:40:19 loop

Can someone help?

Try (define gen (generator () (for-each yield '(a b c))))

I think the issue is this (last line in the docs): > The eventual results of _body_ are supplied to an implicit final https://docs.racket-lang.org/reference/Generators.html#%28def._%28%28lib._racket%2Fgenerator..rkt%29._yield%29%29\|yield; after that final https://docs.racket-lang.org/reference/Generators.html#%28def._%28%28lib._racket%2Fgenerator..rkt%29._yield%29%29\|yield, calling the generator again returns the same values, but all such calls must provide 0 arguments to the generator.

As for why map
doesn’t work…
generator
works both ways. So you can do something like this:
> (define gen (generator () (println (yield 1))))
> (gen)
1
> (gen 42)
42

Because it works both ways, when you use it inside map
, it needs a value to be mapped.

So you can’t just invoke it with no argument

for-each
doesn’t expect a value to be mapped, so it works fine.

aha so the argumewnt of gen
is the return value of yield

ok

makes sense

This aside, you probably shouldn’t use map
anyway, since it constructs an output list, which you are not gonna use anyway.

What I wanted to do is a frontier-based graph traversal where the user has the chance to modify the frontier at each step. Generators are even better for the job than I thought.

The only thing that wories me slightly looking at the docs of racket/genrator is the lack of functions for combining generators. Are generators inherently not composable or is it something else?

you mean something like yield from
in Python?

Personally, I just write:
(define (yield-from xs)
(for ([x xs]) (yield x)))

The performance is probably going to be horrible

But for small programs it probably works fine

Speaking of graph traversal, have you looked at the graph
package? I think their interface is pretty flexible, but I don’t know if it suffices your requirement

Is generator performance generally crappy ?

Super crappy in Racket BC. Kinda OK in Racket CS. Still, I still had an old habit to avoid it whenever possible.

I have looked at the graph package but my graph is actually an AND-OR DAG which is just different enough from the general graph interface that I decided it’s not worth it…

I considered generators to be lightweight transducers, is it the other way round?

or does this abstraction just have crappy performance whatever way you cut it?

I think you are right that generator is a lightweight transducer. I don’t have much experience with transducer, only knowing that it’s more expressive.
Generator’s poor performance in Racket BC is from how Racket implements it by using continuation, and the performance of it in Racket BC is not great.

I don’t know what BDD is. I have this function that traverses a list that satisfies the contract (graph/c node/c ((set/c node/c) . -> . (set/c node/c)) . -> . void?)
. The first arguments are a graph and the starting node and the third argument is a callback that the caller can use to modify the frontier at every step (eg by returning an empty frontier to stop the traversal, by pruning thew frontier, or traversing “hidden” edges, etc).

In the callback the caller collects whatever information they need in the closure of the procedure

I feel like there is probably a better way to do this so I was looking to, instead of accepting a callback argument, make the whole thing a generator

(callback argument == yield)

One pattern I’m used to doing in CL that I can’t seem to emulate in racket (with for
) is this (using Racket for
as the example):
(for ([x xs] [r (rest xs)])
(if (null? r)
;; x is the last element of the list
;; x it not the last element
))
This fails in racket since r
terminates one element too soon. Is there a for
pattern in racket I can use to accomplish the same result (without resorting to index tracking and comparing to length, which may be large and O(N) to compute)?

One very hacky solution is to use in-sequences
to chain (rest xs)
with a sequence of one element.

Not sure if you’re testing the right thing with (null? r)
, since your clause is equivalent to [r (in-list (rest xs))]

Well, I think @massung meant a special value that can be recognized somehow

It’s an example, in CL the above would work fine since r
would just be nil
after being exhauseted

The in-sequences
might work ok for me

Personally I often use a custom in-list+rest
(https://github.com/Metaxal/bazaar/blob/master/loop.rkt#L309). I think something like this should be standard

Also in-zip
(same file), where the parsed elements are pushed in reverse order to another list that is available, so you have [(left element right) (in-zip lst)]

Then you know if you’re on the first element or the last or not.

It depends on what you want to do, but generally I dislike:
for (i in <e>) {
if (p(i)) <do-A> else <do-B>
}
because you can split that into:
for (i in <e-A>) {
<do-A>
}
for (i in <e-B>) {
<do-B>
}
and it looks clearer to me.

But the number of loops can explode if you have several conditions on several clauses

in this particular case, I basically want a (map add1 xs)
to all elements except the last element of xs

You can always resort to a let loop
of course

obviously there are other good use-cases for the above, but this one is simple if anyone has a quick suggest :wink:

For this particular case, I personally would write:
(for/fold ([e #f]) ([x xs] [y (rest xs)])
<do-something-with-x-and-y>
y)

currently a recursive is what i do

then use the value of for/fold
, which is the last element, outside the loop

@massung That would give (for ([(x rst) (in-list+rest xs)])
(if (null? rst) x (+ 1 x)))
as you wished.

Then there’s no need to have a conditional inside the loop

All good suggestions I’ve thought about. Each of them - at the end, though - ends up being an append
or reverse
at the end. Maybe i’ll just suck it up tho

reverse, split-at, map add1, cons, reverse :slightly_smiling_face:

yeah, was looking at it. That would definitely work for this case

Oh, I assumed that you are using for
for effect, which you don’t need append
or reverse
.

But if you use for/list
… you might want to build the list in a reversed order in for/fold
and then reverse it yourself

I need (2 2 2) -> (3 3 2)
basically

Note that this is exactly what for/list
does under the hood

Ah, I just saw this.

To be clear, I totally have a working solution for my problem using a recursive function. I was just frustrated that this couldn’t be done (easily - from what I know) using for/x
. So figured I’d see if anyone here had solved it using a for-construct.

Is it just me or is https://racket.discourse.group/ showing a “blank” page? With JavaScript enabled.

This is the ~3rd time in a week I’ve wanted a for loop that knew whether or not it was at the last element of the list

you surely meant disabled?

Blank for me

Oh… yeah, it’s blank for me too

Seems due to Uncaught Error: Could not find module `handlebars` imported from `discourse-common/lib/raw-handlebars`
c _vendor-f4425d4b17932067d5c6d206656c04d9.js:22
c _vendor-f4425d4b17932067d5c6d206656c04d9.js:22
findDeps _vendor-f4425d4b17932067d5c6d206656c04d9.js:34
c _vendor-f4425d4b17932067d5c6d206656c04d9.js:22
findDeps _vendor-f4425d4b17932067d5c6d206656c04d9.js:34
c _vendor-f4425d4b17932067d5c6d206656c04d9.js:22
requireModule _vendor-f4425d4b17932067d5c6d206656c04d9.js:16
<anonymous> _start-discourse-81637605c9aa837e31086f280d3afdaa.js:18
<anonymous> _start-discourse-81637605c9aa837e31086f280d3afdaa.js:16
<anonymous> _start-discourse-81637605c9aa837e31086f280d3afdaa.js:7
_vendor-f4425d4b17932067d5c6d206656c04d9.js:22:30

RIP

lol


because it’s also blank

It’s been awhile since I saw Inception but I think we need to elicit a sensation of falling to escape?

I’d be sad if I have to write:
(for/fold ([acc '()] [last-elem #f] #:result (reverse (cons last-elem acc)))
([x xs] [y (rest xs)])
(values (cons (add1 x) acc) y))

Plus it’s wrong when there’s only one element :face_palm:

Is there a version of for
where it doesn’t terminate early but keeps iterating w/ default values for consumed sequences until all sequences are consumed?

I could see that being quite useful

I’ve been wanting that too

in-cycle
?

Not sure the difficulty in adding that functionality to for
. I wonder if just an optional 3rd parameter to each iteration would work?
(define (zip/default xs ys)
(for/list ([x xs #f] [y ys #f])
(cons x y)))

in-cycle repeats forever, so it’d never terminate, right?

for will terminate as soon as the first sequence terminates

Right, but it’d be nice if we could make it not (w/o having to know which sequence is the shortest)

so you would have a second sequence in parallel to in-cycle

(in-sequences (in-list xs) (in-cycle (in-value #f)))
should do what you want

“after xs, cycle through #f”

(edit: forgot in-value
)

So, that would work perfectly for my original use case. In the general case, no, though, because it required ahead-of-time knowledge which sequence is longer.

I’m going to use your suggestion now, though, in place of my recursive function :wink:

You can use (require bazaar/loop)
, but feel free to copy the code

You could If none of your sequences would normally contain #f
then you could add a #:break
that stops when all loop variables are #f
(and if they might contain a #f
then you can whip yourself up a unique sentinel by making a (list #f)
and checking for it with eq?
)

These reverted commits possibly intended to fix but no effect yet in production: https://github.com/discourse/discourse/commit/ea37b30ab2c1e7f565a353b5d45f0f760f7f7d2e https://github.com/discourse/discourse/commit/c985f821746a031a537329bec49c384c52d3fb26

Racket Discourse is back!

It back now

(let ([xs* (reverse xs)])
(reverse (cons (car xs*)
(map add1 (cdr xs*)))))
But that won’t generalize. If the data at the end is somehow special, maybe putting it at the end of a linked list is the wrong choice?