Fork me on GitHub
Lennart Buit00:03:15

I like to think of reducing as the act of changing the dimensions of my collection

Lennart Buit00:03:03

if you keep the same number of collection members, map is way easier for that ^^

Lennart Buit00:03:22

and, there is also map-indexed, which I think is what you are looking for

Lennart Buit00:03:29

oh… yeah that changes things

Lennart Buit00:03:44

would nested reduce-kv solve your issue? Like calling reduce-kv inside the reducing function of an outer reduce-kv?


@deleted-user probably easier if you used a different representation for your data. Perhaps a map keyed on the X,Y coordinates as a pair?


The other day I saw that clojure allows metadata to be appended to data, this does not affect the data during comparison.


After reading that I wondered how some of you use this ability in your programs? What is a cool way you have heard of someone using metadata?


@j3elam Probably the coolest thing that comes to minds right now is that you can add metadata to data so that the data can participate in a protocol. For example, with datafy and nav which are new in Clojure 1.10.


@deleted-user I thought you were already doing the loops to get x and y...


Since it's a 2D grid, you pretty much have to address it via x and y tho', right?


My suggestion was meant to make it easy for you to make multiple changes easily since you can assoc or update against the entire hash map (maze) at any point, using just [x y] keys for whatever x and y you already have on hand.


when-not returns nil, such that if relevant-directions also contains nil for whatever reason, your final set would also end up with nil which I’m guessing is not desired behavior


@deleted-user If you treat the tests as a sequence, you can get rid of all the ifs/whens

(defn- valid-directions
  "Return a seq of valid directions based on current position"
  [n-cols n-rows [x y]]
  (let [boundries [[:east  (= x (dec n-cols))]
                   [:west  (zero? x)]
                   [:north (= y (dec n-rows))]
                   [:south (zero? y)]]]
    (map first (remove second boundries))))


I have a largish number of instances of the following pattern in my code:

(let [jn (partial str/join \newline)
      ja (fn [& r] (jn r))]
   (for [:binding-or-coll-exprs]
     (ja (format "foo")
         (format "bar")
         (format "baz")))))
(rewritten from memory, no guarantees I haven’t missed something) where I essentially want to do a for comprehension which should produce a \newline separated string instead of a lazy sequence (as the normal for does). After enough instances of the above (they are producing a report in text format) this makes me wish for a macro to perform the job, say sfor where you could just do:
(sfor [:binding-or-coll-exprs]
      (format "foo")
      (format "bar")
      (format "baz"))
…`for` is quite complex and I’m a total macro noob…how would I go about writing such a macro and are there any pitfalls or reasons not to?


What's ja for?


It seems like your putting new lines between foo bar and baz, and then another new line between group of these?


I'd say this seems a pretty custom use case. I feel macros are best used for more generic cases.


This does seem a bit specific, but writing the macro is pretty straightforward.

(require '[clojure.string :as str])

(defmacro fors
  {:style/indent 1}
  [bindings & body]
  `(str/join \newline
             (for ~bindings
               (str/join \newline [~@body]))))


The style/indent thing is so emacs (and maybe other editors?) knows that it should be indented like for. It's not necessary for it to work.


@U0J9LVB6G wow!! {:style/indent 1} seems to work in cursive as well !!! awesome : )


I think I saw somewhere that it didn't work in cursive, he must've added support for it, that's awesome.


errr…seems I was mistaken…no support in cursive. That was me playing around with indentation settings


and thank you both for the feedback…and apologies for an example which did not reflect the actual problem…the actual code is way more complex than the foo bar example above


I guess the question to ask yourself is if the readability and composability loss from the macro worth the trade off.


There's good cases for macros, and it's hard to judge without knowing the details. But, the common: data over fn over macro applies 😋


For example, it's much easier to stringify a sequence, then it is to sequencify a string


So having sequences until you are absolutely ready to stop processing it is more flexible


So in general. It would be better to have a sequence of sequences. And when you are done, have one function that just generates your string report from it


Most complex views are handled like that.


Think HTML/CSS for example.


One is just an abstract syntax tree. The other defines how that is displayed


So if you had a sequence of sequences. You could render it so that each sequence is separated by new line. But you could also say separate by comma, or comma followed by newline, but no comma in last. Or newline + 4 space indents on every nested sequence. Etc.


You could also choose to render it as rich text, or html, colorize it, etc.


I’m assuming this is not doable in a function…


Hey @U4VDXB2TU! At first glance, no, won’t work as a fn with that syntax, since fns evaluate their args (so it’ll try to evaluate :binding-or-coll-exprs). Often the easiest way to write a macro is to start with something that’ll spit out a hardcoded example of what you want, so in your case

(defmacro sfor []
 `(let [jn (partial str/join \newline)
        ja (fn [& r] (jn r))]
     (for [:binding-or-coll-exprs]
       (ja (format "foo")
           (format "bar")
           (format "baz"))))))
(note the preceding backtick). Then start adding args and swapping out hardcoded values for the ~-evaluated version of those args. You’ll probably want to spend a bit of time beforehand thinking or reading about how quoting works, and the difference between ~x, ~'x, and '~x, because you may encounter issues where your args are evaluated too much or not enough. That’s all right off the top of my head; take it with a slight grain of salt. Good luck!


Use macroexpand and macroexpand-1 frequently while you’re figuring it out, btw, totally indispensible.


thank you. Been playing around with this and ended up with:

(defmacro sfor [seq-exprs & body-exprs]
    (for ~seq-exprs
       (list ~@body-exprs)))))
which seems to do what I want


now if I could only get cursive to grok my custom macro instead of thinking it makes my code invalid…


Nice work! Awesome that you managed to come up with that (list ~@body-exprs), that must have taken some head-scratching.


thank you. I could not get it to work without explicitly calling (list, not sure, perhaps there is a way but this is as far as I got : )


In the other thread I wrote an implementation that's the exact same except I used a literal vector instead of list. So that works as well. I think it doesn't matter which one. I feel like clojure has that pythonian quality of there being one obvious way to do things, because I see this a lot, different people implementing the same thing the same way.


ah did not consider a vector, thanks for the pointer

Kari Marttila09:03:58

It is sunday and the weather is bad in Helsinki which means it's time to do some Clojure exercises! I'd like to start a new personal Clojure project. Something that I can start using deps.edn and also explore how to use protocols efficiently, and also practice with the Clojure sequence library (as instructed in "Programming Clojure", 3rd ed. page 85 - I now realize that I have used far too much recur instead of figuring out how to do the same thing using the Clojure sequence library). I'd like to use AWS services with Clojure and also experiment how to use Cognitect's AWS library. Any recommendations for the new exercise?


I don’t have an answer to your question but wanted to ask if you could mention some examples where you used recur but the sequence library would have been better? I feel like I might be missing out

Kari Marttila10:03:22

"Programming Clojure" 3rd edition says in page 85 in the 2. instruction: "Use recur when you're producing scalar values or small, fixed sequences." I understood that in most cases when not producing scalar values you are able to use the sequence library to do various transformations if you know the library well. This is something that I'd like to focus next - I have a feeling that I have just used recur as a default method to process any transformation that I couldn't find simple map or filter type processing.

Kari Marttila10:03:47

I don't have any new project ideas in mind. So, I think I'm going to make a second implementation of my Simple Server ( that I originally created for studying how to use Clojure for implementing a web server. This time I'm going to use deps.edn and Cognitects's new AWS library. And I'm also going to review my own code regarding instructions in "Programming Clojure" 3rd edition in page 85 - if I can figure out more ways to use the Clojure sequence library instead of processing things with recur.


Starting with recur is normal. But eventually, you develop a vocabulary for the higher level sequence processing functions, and realize a lot of the recur use cases can be handled by a composition of them instead.


Can you give an example of some of those functions? I’m just not sure which ones you guys are talking about. Just want to gauge my understanding of the library.


Or it could just be because I’m mostly doing number theory stuff for the time being that I’m finding loop-recur to be more performant.


@U0K064KQV ah, yeah, I looked at that page and it left me scratching my head because I am fairly familiar with most of those and have the opposite problem—wouldn’t know how to write them as a loop-recur 😅