# Map versus for/list (& idiomatic Racket, the "for/..." zoo, etc)

**URL:** https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831
**Category:** General
**Created:** [July 3, 2025, 6:37pm UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831 "2025-07-03T18:37:35Z")
**Posts on this page:** 13
**Page:** 1

<div class="post-metadata">

### Author: ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)
#### Post date: [July 3, 2025, 6:37pm UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/1 "2025-07-03T18:37:35Z")

</div>

I'm learning Racket, and I do know about functional programming, and have used and loved `map` (and its relatives) in many languages (Haskell, emacs lisp, Typescript/JS, Python, Julia...even C#, where they call it `Select`...)

In Racket, you have `map` but also `for/list`. I don't want to get super into the weeds on the difference, so here's my understanding for what I should do, as someone at my level:

- use `for/list` unless you know `map` would be better.
- it may not be as fast, but (1) premature optimization etc; and (2) it's more idiomatic, since it's consistent with the `for/...` ecosystem: `for/and`, `for/vector`, `for/first` and so many others.

Does that sound about right?

And I do find all the "for" things a bit overwhelming. What's your advice for someone new to Racket about using `for`, `for/list`, together with `in-list`, `in-range`, and those guys?

---

<div class="post-metadata">

### Author: ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)
#### Post date: [July 3, 2025, 6:54pm UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/2 "2025-07-03T18:54:02Z")

</div>

Map is great for applying a function to all elements in a list and collecting the results in a list.

A simple version of `map` could be written:

```scheme
(define (map f xs)
   (for/list ([x (in-list xs)])
      (f x)))

```

I expect `map` and a map-like `for/list` to be equally fast.

Using `map` is succinct but the `for/list` is often easier to work with,  
if you need "something almost a map".

For example, if we want to map of the even number of list, then we can write:

```scheme
(for/list ([x (in-list xs)] 
                #:when (even? x)]) 
   (square x))

```

Similarly, if we want to "map" over two lists with different lengths, we can do:

```scheme
(for/list ([x (in-list xs)]
                [y (in-list ys)])
   (list x y))

```

Here the loop stops when one of the lists `xs` or `ys` are exhausted.  
For `map` the lists need to be the same length.

Finally, if the result isn't a list, with `map` you need to convert the result  
using, say, `list->string`. With for you can simply switch from `for/list` to `for/string`.

The best thing you can do, is to experiment writing loops with the `for`-constructs.

---

<div class="post-metadata">

### Author: ![jjsimpso](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jjsimpso/32/602_2.png) [@jjsimpso](https://racket.discourse.group/u/jjsimpso)
#### Post date: [July 3, 2025, 7:36pm UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/3 "2025-07-03T19:36:55Z")

</div>

I've also felt overwhelmed at times by the `for/...` zoo (love that term!) So much that when learning racket I mostly stuck to named lets or recursive functions.

After toying with racket for a few years I finally began to gravitate toward `for`, but I tend to stick to a few favorites. I don't have a functional background so I avoided `for/fold` for a long time but it is quite useful and not scary at all! I still have to look things up quite often though.

---

<div class="post-metadata">

### Author: ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)
#### Post date: [July 3, 2025, 8:31pm UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/4 "2025-07-03T20:31:41Z")

</div>

> [@soegaard](#):
>
> For example, if we want to map of the even number of list, then we can write:
> 
> ```scheme
> (for/list ([x (in-list xs)] 
> #:when (even? x)]) 
> (square x))
> 
> ```

Am I right that in "classic" functional programming, you'd just use filter, right? Something like this pseudo-code:

```scheme
(map (square x) (filter even? (in-list xs)))

```

Wrapping the `in-list` expression in a filter seems effectively the same as the `#:when (even? x)`.

I'm sure there are implementation details, related to speed and sequences, streams, lists, and so on. One thing I like as a beginner with `map` and `filter` for this example is, I don't have to remember the unintuitive `#:` syntax. I'm used to the "`filter predicate list-like-thing`" pattern.

But, yes, I do aim to use and get better with `for/list`.

(Coincidentally, I'm reading the lovely _Don't Teaching Coding Until You Read This Book_, and it emphasizes that programming languages really _are_ languages, and just like one may speak a human language fluently and idiomatically, one should aim to "speak" a programming language fluently and idiomatically.

Compare a sentence said by a nonnative English speaker with some minor grammar errors, but that's understandable; or a sentence that uses a strange word order, or is spoken with a difficult-to-understand accent...with Racket code that would use `map` and `filter` for your "only evens" example. The English and Racket examples both "work", but in each case, the fluent, idiomatic, elegant way to express the idea is somewhat different.)

---

<div class="post-metadata">

### Author: ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)
#### Post date: [July 3, 2025, 8:33pm UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/5 "2025-07-03T20:33:42Z")

</div>

> [@soegaard](#):
>
> imilarly, if we want to "map" over two lists with different lengths, we can do:
> 
> ```scheme
> (for/list ([x (in-list xs)]
> [y (in-list ys)])
> (list x y))
> 
> ```

That reminds me of Python's `itertools` -- I recall needing to use that for various variations on Python's `zip`: use different-sized lists and truncate at the shorter; or use the longer list and a specified dummy/filler element for the extra elements "off the end" of the shorter list, and so on.

So, Racket kinda has that built-in. Nice.

---

<div class="post-metadata">

### Author: ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)
#### Post date: [July 3, 2025, 8:35pm UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/6 "2025-07-03T20:35:08Z")

</div>

> [@jjsimpso](#):
>
> I don't have a functional background so I avoided `for/fold` for a long time but it is quite useful and not scary at all! I still have to look things up quite often though.

`fold` in functional programming can seem complicated, but it's not too hard to understand, and it's _super_ fundamental. There's a staggering number of things you do with `fold`.

---

<div class="post-metadata">

### Author: ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)
#### Post date: [July 3, 2025, 9:09pm UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/7 "2025-07-03T21:09:05Z")

</div>

Yes. The `#:when` is a filter.

When you are experimenting with a loop, to me it  
is easier to add/remove clauses to a for-loop than  
working with nested function calls.

But! If you are into (classical) functional programming,  
take a look at Qi in the Racket docs. It's a library  
made to make that style both convenient and  
efficient at the same time.

---

<div class="post-metadata">

### Author: ![notjack](https://avatars.discourse-cdn.com/v4/letter/n/e47774/32.png) [@notjack](https://racket.discourse.group/u/notjack)
#### Post date: [July 3, 2025, 10:50pm UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/8 "2025-07-03T22:50:16Z")

</div>

If you're interested in more specific guidance on `map` vs `for/list`, the rules I made [Resyntax](https://docs.racket-lang.org/resyntax/) follow are roughly these:

- **Never** rewrite `for/list` into `map`
- **Don't** rewrite `map` into `for/list` if it's not using a lambda
- **Don't** rewrite `map` into `for/list` if the whole expression fits on one line
- **Do** rewrite `map` into `for/list` if it's using a lambda with multiple body forms
- **Do** rewrite `map` into `for/list` if it's using a lambda with a `let` inside that can be rewritten into `define`.

I find that captures the average Racketeer's style expectations pretty well. You can see some examples of how those rewrites look in [these test cases](https://github.com/jackfirth/resyntax/blob/master/default-recommendations/for-loop-shortcuts-test.rkt).

---

<div class="post-metadata">

### Author: ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)
#### Post date: [July 4, 2025, 1:15pm UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/9 "2025-07-04T13:15:19Z")

</div>

Oooh, I like the idea of that -- it seems like a fancy version of a formatter (like, say, `gofmt`, or `prettier`). I like that it will actually rewrite your code -- I could imagine a very nice workflow where I `git add` my working code, then run Resyntax, and look at the diff and stage the changes I want.

---

<div class="post-metadata">

### Author: ![gus-massa](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/gus-massa/32/507_2.png) [@gus-massa](https://racket.discourse.group/u/gus-massa)
#### Post date: [July 14, 2025, 2:14pm UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/10 "2025-07-14T14:14:39Z")

</div>

> [@ddrake](#):
>
> Wrapping the `in-list` expression in a filter seems effectively the same as the `#:when (even? x)`.

Using filter creates an intermediate list, so if the main part is fast I expect `map` and `filter` to be almost x2 slower than `for/list` and `#:when`

This duplication is very different to optimize away, and @soegaard said you should take a look at [Qi: An Embeddable Flow-Oriented Language](https://docs.racket-lang.org/qi/index.html) if you prefer this code style.

The nice part of `for/vector` is that it handles all the details like creating a vector of the right size in a way that is efficient. All the `for/...` forms hide the tricks to get efficient allocations of the results.

Initially, you can ignore `in-list`, `in-...` and Racket will use the right iteration at run time. Later you may preffer to add `in-list`, `in-...` to much get faster code.

---

<div class="post-metadata">

### Author: ![shawnw](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/shawnw/32/1031_2.png) [@shawnw](https://racket.discourse.group/u/shawnw)
#### Post date: [July 15, 2025, 9:37am UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/11 "2025-07-15T09:37:20Z")

</div>

If you use `(sequence-filter even? (in-list blah))` there's no intermediate list and it should be similar performance if not identical to using `#:when` and `in-list` (Well, maybe a bit slower as I think `in-list` gets macroexpanded into inline list manipulation in a `for`)

---

<div class="post-metadata">

### Author: ![gus-massa](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/gus-massa/32/507_2.png) [@gus-massa](https://racket.discourse.group/u/gus-massa)
#### Post date: [July 15, 2025, 11:58am UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/12 "2025-07-15T11:58:06Z")

</div>

I expect `sequence-filter` to be much slower than all the other alternatives. It uses very generic code for sequences, and all the magic is gone.

---

<div class="post-metadata">

### Author: ![shawnw](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/shawnw/32/1031_2.png) [@shawnw](https://racket.discourse.group/u/shawnw)
#### Post date: [July 15, 2025, 6:07pm UTC](https://racket.discourse.group/t/map-versus-for-list-idiomatic-racket-the-for-zoo-etc/3831/13 "2025-07-15T18:07:55Z")

</div>

I wouldn't expect it to be that much slower if you specialize the sequence type with `in-list` instead of having it figure it out itself. Let's see, with some ways to count the number of even numbers in a list...

```scheme
#lang racket

(require srfi/171) ; from my extra-srfi-libs package

(define data (build-list 5000000 (thunk* (random 1000000))))

(collect-garbage)
(time (printf "for/sum: ~A " (for/sum ([elem (in-list data)] #:when (even? elem)) 1)))
(collect-garbage)
(time (printf "length + for/list: ~A "
              (length (for/list ([elem (in-list data)] #:when (even? elem)) elem))))
(collect-garbage)
(time (printf "length/filter: ~A " (length (filter even? data))))
(collect-garbage)
(time (printf "sequence length/filter: ~A " (sequence-length (sequence-filter even? data))))
(collect-garbage)
(time (printf "sequence length/filter/in-list: ~A "
              (sequence-length (sequence-filter even? (in-list data)))))
(collect-garbage)
(time (printf "count: ~A " (count even? data)))
(collect-garbage)
(time (printf "sequence count: ~A " (sequence-count even? data)))
(collect-garbage)
(time (printf "sequence count/in-list: ~A " (sequence-count even? (in-list data))))
(collect-garbage)
(time (printf "transducer: ~A " (list-transduce (tfilter even?) rcount data)))

```

timings:

```none
$ racket list-benchmarks.rkt
for/sum: 2499672 cpu time: 46 real time: 43 gc time: 0
length + for/list: 2499672 cpu time: 78 real time: 80 gc time: 15
length/filter: 2499672 cpu time: 78 real time: 78 gc time: 31
sequence length/filter: 2499672 cpu time: 937 real time: 972 gc time: 78
sequence length/filter/in-list: 2499672 cpu time: 1484 real time: 1542 gc time: 62
count: 2499672 cpu time: 31 real time: 30 gc time: 0
sequence count: 2499672 cpu time: 93 real time: 85 gc time: 0
sequence count/in-list: 2499672 cpu time: 93 real time: 85 gc time: 0
transducer: 2499672 cpu time: 4125 real time: 4292 gc time: 15

```

Huh. Yeah, guess you were right.
