
I’m trying to figure out, so far without success, how the multiple id form of a for
loop works. I read https://docs.racket-lang.org/reference/for.html and https://docs.racket-lang.org/guide/for.html
The reference says "[…] the sequence produced by a seq-expr must return as many values for each iteration as corresponding ids." How does a sequence return anything? :thinking_face:
Anyway, I tried (for ([(i j) '((1 2) (3 4))])
(display (list i j)))
which gives me result arity mismatch;
expected number of values not received
expected: 2
received: 1
in: local-binding form
arguments...:
and I tried (for ([(i j) (list (values 1 2) (values 3 4))])
(displayln (list i j)))
but in DrRacket that highlights the (list ...)
and gives me the message result arity mismatch;
expected number of values not received
expected: 1
received: 2
which I can’t interpret in any helpful way.
Then, the documentation gives the example (for ([(i j) #hash(("a" . 1) ("b" . 20))])
(display (list i j)))
which prints key/value pairs, but I don’t know how the hash interacts with the for
loop. Since #hash(...)
by itself is just the hash, the use in for
just looks like “here’s some magic you can do with a hash in a for
loop.” :wink:
What would most likely help me is how in-my-list
would need to look in the following example: (define my-list '((1 2) (3 4)))
(define (in-my-list lst)
; What to put here?
...)
; Displays `'(1 2)` and `'(3 4)`
(for ([(i j) (in-my-list my-list)])
(display (list i j)))

I think this would work:
(define (in-my-list lst)
(sequence-map values lst))

Oops, I think it needs to be:
(define (in-my-list lst)
(sequence-map (lambda (entry) (apply values entry)) lst))

A Racket sequence or Racket stream, unlike a list or most other collections, can have elements which consist of multiple values. The elements of a stream can be computed on demand, and the behavior that computes an element can end up computing multiple values for that element.

Thanks! Yes, I now see that (list (values 1 2) (values 3 4))
as such gives me an “arity mismatch.” Different from what I had assumed, it didn’t have anything to do with the for
context.

Yeah, the result of (values 1 2)
is two values, but function arguments (and almost everything else) have to be one value. Racket treats this similarly to passing two arguments to a function when only one is expected, hence the arity error.

To derive a “recipe” from your answer, if I have a list where each item is a sub-list representing the values of a sequence item, I can define (define (list->sequence lst)
(sequence-map
(lambda (entry)
(apply values entry))
lst))

It’s not as intuitive as I had hoped, but it certainly helps. :slightly_smiling_face:

IIUC with this unfortunately you’ll traverse the whole outer list once with sequence-map
, and again with for
?

Myself, instead I’d probably use match-define
in the body of the for
to destructure each entry. Like
(for ([entry (in-list lst)])
(match-define (list i j) entry)
___)

It would be neat if for
forms let you use a match expression as the lhs.

Something like (for ([(match-define i j) (in-list lst)]) ___)

Or something even less verbose than that.

Unfortunately I don’t think it can be quite as concise as you were (understandably) hoping: (i j)
. :slightly_smiling_face:

> IIUC with this unfortunately you’ll traverse the whole outer list once with sequence-map
, and again with for
? The documentation of sequence-map
says: > Returns a sequence that contains f applied to each element of s. The new sequence is constructed lazily. So my understanding is you’d only traverse once.
By the way, if it wasn’t for this sentence in the documentation, my follow-up question would have been what I have to do in case of infinite sequences :wink: , but this seems already to be covered by sequence-map
.

match-define
looks interesting. Could be that I saw it at some point, but Racket has so many small functions and forms for similar things that I keep forgetting about these over and over. :confused:

> Myself, instead I’d probably use match-define
in the body of the for
to destructure each entry. Like If there hadn’t been a sufficiently succinct solution, I also might have gone with just iterating over a list and deconstructing it somehow. But I also thought about making the binding implicit. I first thought of a macro and then saw that for
already supported more than one id for the “iteration variable(s)”, and this caused this thread. :slightly_smiling_face:

Sorry for ranting, but my impression is that the multiple values concept complicates so many things. As an example from this thread, iterating over a list with “single values” per item is super-simple (as it should be), but iterating over several values per item is completely different. I wonder if it wouldn’t have been better to just use lists to combine multiple values and have an easy way to “destructure” them, as with match-define
. This would seem like a more orthogonal design to me.

Is there any “tolerant” implementation of the take
function (& friends)? I mean (take '(0 1 2) 5)
fails cause the list doesn’t have 5 elements. E.g. in clojure (take 10 '(1 2 3))
returns (1 2 3)
…, however the rackjure package doesn’t have it and IMO re-implementing my own wheel is just pointless, even if it’s just two or three LOC.

There’s no included version, sadly. Usually what I do is use a for loop:
(for/list ([x lst] [_ (range n)]) x)

@xlambein has joined the channel

I don’t know the history but: I think one motivation was things like hash-tables and dictionaries where each element has known multiple values (the key and the value) so it is handy to write [(k v) (in-hash ht)]
. Another motivation was the for
and in-hash
syntax would expand to code as performant as what you’d write by hand. Using multiple values means a list isn’t allocated to stuff them into… only to take them right out again.

Someone else could probably explain the history better, and, explain this in a more principled way, but that’s my understanding.

Hey everyone, I was curious about something: how does a module print out all its top-level expressions? I saw in the docs how to en/disable that behaviour, but I can’t find the details of how it’s actually performing this.

Some #lang
s like racket/base
supply a #%module-begin
form that does this: https://docs.racket-lang.org/reference/module.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._~23~25module-begin%29%29

@mildc055ee has joined the channel

Thanks! Do you know where I can find the source code of #%module-begin
for racket/base
?

Or of any link in the docs, actually. Is there a way to “go to source” from a doc page?

I couldn’t find my way around the racket/base
source, but I managed to dig it up in the scheme
source. For those interested, here’s a wrapper that adds a print statement to each expression: https://github.com/racket/racket/blob/448b77a6629c68659e1360fbe9f9e1ecea078f9c/racket/collects/racket/private/modbeg.rkt

AFAIK in general there’s not a “go to source” from docs.


Yes that looks like it.

Yeah that’s what I usually try to do, but the source can be really hard to navigate

Also you can use raco expand
or the Macro Stepper in Dr Racket to see how things expand. Sometimes how they expand is as interesting (or more) than seeing the macro source code.

oh yep maybe that would’ve been a good idea! I’ll try that next time, thanks for your help!

If you’re never used the macro stepper, and/or if you’re interested in questions like the one you asked, it can be interesting to take a super simple .rkt program and expand that.

Even a program like #lang racket/base
42
:slightly_smiling_face:

The docs at https://docs.racket-lang.org/reference/module.html say > No identifier can be imported or defined more than once at any phase level within a single module, except that a definition via define-values or define-syntaxes can shadow a preceding import via #%require; unless the shadowed import is from the module’s initial module-path, a warning is logged to the initial logger. Is that still true?
If I PLTSTDOUT=warning racket foo.rkt
and foo.rkt
is (say) #lang racket/base
(require net/url)
(define get-pure-port 42)
get-pure-port
I don’t see any warning? Nor when I use PLTSTDOUT=debug
and look through all of that output.

This isn’t of any practical importance to me. I just noticed it and wondered if the doc needs to be updated, or I’m doing it wrong, or whatever. Just curious.

I’ve used it a couple of times, but I have yet to understand in which situations it can be useful. I’ll try expanding a small program to see, that sounds interesting!

I think you’re right that a warning is no longer logged. (I don’t remember it ever being logged, but my lack of memory merely suggests how the current expander ended up not logging anything.)

There are two questions here: why do multiple values exist, and why does the for family provide support for them the way it does

The answer to the first question is that it is mostly about efficiency — you don’t have to allocate a data structure for the multiple values

But also it adds symmetry in the presence of continuations — multiple values is like calling the continuation with multiple arguments

Given the existence of multiple values, they’re sort of a natural thing to add to for

And they let you avoid some inefficiency in things like hash iteration

I think a different design featuring pervasive pattern matching, built in tuples, and a compiler that could eliminate intermediate tuples is potentially better

But that’s a quite different language and wasn’t the choice made 30+ years ago

I don’t remember any logging

Racket in general doesn’t have a great story for warnings

This was given to me when I was interested in inspecting modules from other code: https://gist.github.com/shhyou/726d0542aef45b13981173a334a0202c

@samth Thanks for the info. I feel less grumpy now. :slightly_smiling_face: