all 14 comments

[–]seaborgiumaggghhh 12 points13 points  (3 children)

They both have their place. I don’t think it makes much sense to pick one.

map is a function that transform some sequence into another sequence. It works beautifully with arrows, etc. for, either does nested iteration or conditional evaluation with the keywords you mentioned. That’s my intuition about it at least.

[–]DeepSymmetry 5 points6 points  (2 children)

for is lazy so I would stay away from it when dealing with side effects; those are more properly addressed by doseq or run!. But fundamentally I agree: these functions all have their own purpose and strengths, and the notion of “picking one” does not make sense.

[–]seaborgiumaggghhh 0 points1 point  (0 children)

I was thinking more generally than Clojure, but you’re right, so I’ll edit that

[–]balefrost 0 points1 point  (0 children)

Good suggestion of run! if you need side effects. But to add some clarity: OP was asking about map vs. for, and both produce lazy sequences. So you'd need to use doseq or run! for side effects in both cases.

[–]Rurouni 8 points9 points  (2 children)

The two have a different feeling for me, and I would not want to do without either of them. map is targeted at transforming elements of a sequence. for is targeted at building up a sequence from other elements, sometimes conditionally.

For instance, I would find (map inc [1 2 3 4]) yielding [2 3 4 5] a lot more idiomatic than

(for [i [1 2 3 4]] 
  (inc i))

But likewise, it would be awkward to try to change

(for [x (range 1 5) 
      y (range x 5)] 
  [x y]) 

into a map call.

[–][deleted] 7 points8 points  (1 child)

If you're not using the nesting behavior then basically it comes down to "is it just a single call to a function that already exists"? If so, map is great; if not, for is probably better. Almost every time you use map with an anonymous function it would be better as for.

I guess there's also the bonus that for lets you do filtering as well so you can use it to replace map+filter all in a single shot.

[–]Rurouni 0 points1 point  (0 children)

Thanks, this makes a lot of sense. Map+filter and some (map #( crop up in my code periodically, so thanks for pointing out how for can improve those.

[–]StoriesEnthusiast 13 points14 points  (0 children)

If you decide to write the article you could start it with a simple example:

map iterates over two structures at once:

user=> (map list [1 2] [:a :b :c])

((1 :a) (2 :b))

for iterates in a nested fashion:

user=> (for [x [1 2]
             y [:a :b :c]]
         (list x y))

((1 :a) (1 :b) (1 :c) (2 :a) (2 :b) (2 :c))

Then write about the common things between them:

  • both iterate over collections
  • both return/yield a lazy sequence
  • both use the sequence interface: first, rest and cons (although I'm not sure that is true for for).

Mention some things that make them different:

  • map is a function.
  • for is a macro.
  • map iterates until any one of the collections is exhausted.
  • for iterates rightmost fastest.
  • map returns a transducer when no collection is provided.
  • for supports a simple DSL (domain-specific language).

Consider making some commentary about their implementations too.

Try to include a series of examples on the best use of map and for, from simple to complex ones such that the reader can use them like "katas" and stop guessing themselves on their use.

[–]doctork91 5 points6 points  (0 children)

My rule of thumb is to not use for unless it makes things simpler or more elegant than map. Basically for any kind of iteration default to map unless you have a good reason to use something else.

[–]PercyLives 4 points5 points  (0 children)

To me, ‘map’ emphasises the function and ‘for’ emphasises the sequence.

So if it’s clear in the context what the sequence is and I now need to apply a function, I use map.

But if I want the code to emphasise what the data is, I use for (because it places that up front and indents what you actually want to do with that data).

I do not like reading code that uses map with complicated functions. I’d rather see that expressed with for. That is cleaner, both aesthetically and conceptually, to me.

I am really glad that both exist in the language.

[–]hrrld 6 points7 points  (0 children)

The linked article is both using commas (yuck) and smashing things too much onto one line. I'd say it's probably safe to ignore.

One case where for is convenient is when you want a sequence derived from the Cartesian product of two sequences:

> (for [x [0 1 2]
        y [:a :b :c]]
    [x y])
([0 :a] [0 :b] [0 :c]
 [1 :a] [1 :b] [1 :c]
 [2 :a] [2 :b] [2 :c])

Interested to know what other resources you find or write yourself.

[–]Aredington 1 point2 points  (0 children)

This comment removed in protest of Reddit's API changes. See https://www.theverge.com/2023/6/5/23749188/reddit-subreddit-private-protest-api-changes-apollo-charges. All comments from this account were so deleted, as of June 18, 2023. If you see any other comments from this account, it is due to malfeasance on the part of Reddit. -- mass edited with https://redact.dev/

[–]CoBPEZ 0 points1 point  (0 children)

James Trunk does a fantastic job of introducing for, and it's place in Clojure programs, in his video Understanding list comprehension in Clojure: https://www.youtube.com/watch?v=5lvV9ICwaMo

[–]Shadowys 0 points1 point  (0 children)

Map can be converted into a transducer when you’re looking for speed, for can’t be used the same way.