Fork me on GitHub
Vincent Olesen11:12:29

I was solving an AOC puzzle, and wrote a part in two ways:

 (for [point points
       neighbour (neighbours point)
       :when (not (contains? points neighbour))]
(->> points
     (mapcat neighbours)
     (remove points)
What would be considered most idiomatic?

Miķelis Vindavs13:12:50

I think it’s a matter of personal preference. Personally, I’d say the second one is more idiomatic and I prefer it. I usually use for loops when there is nested iteration (e.g. (for [x … y …] [x y]))

👍 1

Second one, threads are idiomatic

Miķelis Vindavs13:12:24

imo, the threads also make it clearer what is happening from a data flow perspective and don’t introduce a lot of name bindings (“variables”) which means less noise when trying to understand what’s going on


You can read the threading guide if you haven't done so yet, it has some important points, such as to when use thread first or thread last (

Miķelis Vindavs13:12:22

for example, the first one could have a typo like this

 (for [point points
       neighbour (neighbours point)
       :when (not (contains? point neighbour))]
which is simply not possible in the second one

Miķelis Vindavs13:12:01

for fun, here’s what our new overlords have to say about the matter


these 2 solutions are doing very different things (macro expand the for). for is doing a nested iteration on 2 collections. for each point iterate over neighbours. then the condition in the iteration is sorta also an iteration over points. this is really confusing.


the threaded solution is a lot more clear


you use for when you want to iterate over permutations of your collections data

Miķelis Vindavs18:12:10

They appear to be doing the same thing

Miķelis Vindavs18:12:01

Because neighbour depends on point, it can't be a product


the macro expansion of the for solution will show you that it's doing a lot more than the threaded solution


i looked over the for again, and i just noticed that the second collection is derived from the first. i haven't actually seen something like this before. i wasn't aware for could do that.


i still think that in this case for is being used inappropriately. it's better to use it for permutations.

Miķelis Vindavs18:12:08

What makes it inappropriate?


it's not being used for permutation iteration

Miķelis Vindavs18:12:46

Why should it only be used for that?


because clojure has better tools for those other use cases, like map/filter/reduce

Miķelis Vindavs18:12:02

You can also do permutations with nested maps


maybe there are other good use cases for for could you show me some examples? i've only seen it be good for permutation stuff


yeah, that's what for expands into, nested loops, which look like nested maps when you aren't doing filtering

Miķelis Vindavs18:12:39

My point is - you can do the same thing in either, it's just a matter of preference and readability


yeah, and that's what i'm focused on


for has better readability for permutations than nested anything


if you have good examples of for for other use cases, i'm interested. i imagine it's ok with graph stuff, but then there are graph libs that probably just are better to use.


in this specific use case, the for solution uses 3 nested loops. the threaded solution uses 2


these are not doing the same thing, and also one is more readable than the other. for is not the right tool for this job, and it's less readable as well.


Perhaps off-topic, but is there any way I can peek into the java process that is running my CIDER repl to see if it has open file handles, and things of that sort?


are you looking for a java-specific method? On Linux (and I think macOS) you should be able to do lsof -c java or lsof -p <PID of CIDER process> to list open handles and sockets. On Windows you could use or to do the same from CLI or GUI


lsof works for me