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?
Similarly, if we want to "map" over two lists with different lengths, we can do:
(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.
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.
Am I right that in "classic" functional programming, you'd just use filter, right? Something like this pseudo-code:
(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.)
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.
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.
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.
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.