Fork me on GitHub
#clojure
<
2022-10-07
>
Emil Konstantinovitz09:10:42

Good day everyone. Does anyone know something like integrant but usable in Scala or Java (preferably Scala)?

rolt16:10:53

hmmm Spring 😅 ? the keywords you want are dependency injection or inversion of control frameworks

hoynk18:10:16

Is it possible to exclude a test with lein test? I can only specify a test, but couldn't find a way run all but one test.

Ben Sless18:10:40

You can use test selectors

hoynk19:10:44

Will study those, thanks.

JoshLemer23:10:34

Anyone know of a library that provides a nice macro to get JavaScript-like map construction with keys the same name as the local variables?

const [w, x, y, z] = [1, 2, 3, 4]
{w, x, renamedY: y, z} // {w: 1, x: 2, renamedY: 3, z: 4}
Would be cool to be able to do this in clojure like:
(let [w 1 x 2 y 3 z 4]
  (map-of w x [:renamed-y y] z))
I have a very basic impl here that's probably not good
(defmacro map-of [& syms]
  (->> syms
       (map (fn [x] (cond
                      (symbol? x) [(keyword x) x]
                      (and (vector? x) (= 2 (count x))) x)))
       (into {})))

(let [w 0
      x 1
      y 2
      z 3]
  (map-of w [:renamed-x x] y z))
=> {:w 0, :renamed-x 1, :y 2, :z 3}

JoshLemer23:10:33

oh yeah I know that there is nothing like this in standard Clojure

phronmophobic23:10:03

oh, I guess I didn't totally get what you were trying to do.

JoshLemer00:10:27

I want to construct a map without having to repeat the name of the variables. For instance

(let [foo 1 bar 2 baz]
  {:foo foo :bar bar :baz baz})
I really just want to have a key where each variable is in the map, with a key equal to the keyword of its name. You can do that in JS like
const [foo, bar, baz] = [1, 2, 3]
{foo, bar, baz}
but there's no normal way to do it in Clojure without repeating yourself. I thought, surely I'm not the first one to think of it, but I did make my own macro which lets you do it, but I thought probably someone else has done it much better
(let [foo 1 bar 2 baz 3]
   (map-of foo bar baz))

pppaul00:10:46

there are libs/macros that do this. when clojure was new i came across a few. these didn't catch on. i think it's possible that adding another require to your ns, and a call to a macro, probably outweighs the convince. also your rename thing (map-entry) probably is more common than not.

❤️ 1
seancorfield01:10:19

This pops up in discussions from time-to-time, mostly from folks coming to Clojure from JS I think, and so quite a few people have written macros like this. We've even got one in our "Clojure extensions" library at work (this is open source): https://github.com/worldsingles/commons/blob/master/src/ws/clojure/extensions.clj#L152-L174 but I'm pretty sure we don't use it anywhere in our code now. It just isn't something that seems to come up much in "real world Clojure"...

❤️ 1
seancorfield01:10:43

(ours doesn't have the renaming but you could do (assoc (map-of w y z) :renamed-x x) so I'm not sure the extra "cleverness" of the macro is worth it -- but then I'm not sure the :with/`:without` "cleverness" in our macro is worth it either)

seancorfield01:10:56

We've stopped using almost everything in that library, TBH. We have 7 nses that still use condp->, 1 that uses dissoc-all, and 2 that use interleave-all. The latter has a Jira ticket for consideration for core at some point https://ask.clojure.org/index.php/12125/add-interleave-all-like-partition-all-is-to-partition dissoc-all is just apply dissoc so "why bother?" and condp-> has really proved "too clever" in actual usage. We're actively removing it as we touch code "near" it.

Joshua Suskalo20:10:13

So to extend the idea of "this doesn't come up much in the real world", look at your original example. Why are you giving the values let bound names in the first place? Why not simply return

{:w 1 :x 2 :renamed-y 3 :z 4}
Just directly putting the computation in the map literal is very common and considered idiomatic.

Joshua Suskalo20:10:33

Occasionally it's good for visual flow to have a repeat of the name, but it's not super common.

valerauko09:10:28

(defmacro map-of
    [& syms]
    (zipmap (map (comp name keyword) syms) syms))

(let [a 1
      b 2
      c 3]
  (map-of a b c))
;; {:a 1 :b 2 :c 3}

❤️ 2
reefersleep09:10:33

I was thinking of zipmap, too

JoshLemer05:10:18

Oh sorry didn't see these comments. I dunno, it comes up for me pretty often, and it comes up all the time in JS to the point where they implemented that special syntax and it enjoys extremely wide use like just ubiquitously used all over the place

seancorfield05:10:12

Yeah, it's always been JS people that have asked for it in the past -- but it's not something Clojure folks seem to need -- so it's very interesting to me that the language background leads some people into thinking they need this, but not others from a different background...

JoshLemer05:10:41

I think it's because if you haven't experienced a feature in an other language you're a lot less likely to think of that feature as something you'd want. I'm actually not primarily a JS developer, but a Java/Scala dev by day, but in the small amount of JS I work on I really enjoy that feature of

{w, x, renamed: y, z}
you gotta admit it is very readable and succinct

pppaul05:10:00

i find that my private variable names don't tend to be the same as my return map keys

JoshLemer05:10:25

It's not always possible to construct the whole map with expressions at once. For instance

(let [w (calculate-w 1 2 3)
      x (calculate-x w)
      y (calculate-y w x)
      z (calculate-z w x y)]
   (map-of w x y z))

JoshLemer05:10:06

I think it might just be a case of, it's not the status quo so you don't know how much you're missing it. Think about it like this, when we deconstruct maps, we are all the time assigning the value at a key to an identifier with the exact same name as the key in the map

(let [{:keys [w x y z]} my-hashmap]
  (+ w x y z))
This is super idiomatic and beloved feature of Clojure

pppaul05:10:23

i really think that because we have immutable data, this isn't so common. it's really common to be assoc/dissoc or modifying a map as to creating a new map. I just mostly JS and clj (not cljs) and i really never want a feature like this in my clj code, and also i never use this in JS either, though it's a lot more practical there because i can't change objects like i can in clojure (safely)

pppaul06:10:30

i can see one major issue with this type of syntax. renaming private variables now renames your return values, so you have to be very careful in refactoring

JoshLemer06:10:06

That's very true

pppaul06:10:11

other than that i don't think i have an issue with it. maybe there is something in clojure that makes this hard to implement. i have gotten around the tedious issue of making maps with learning to use my editor better (multi cursors mainly). however macros should be able to do exactly what you want with something like #map {w, x, y, z} which i think is a pretty good compromise

❤️ 1
seancorfield14:10:47

At work we use qualified keywords in maps a lot so they're never going to match local binding symbols, and working with next.jdbc means you're using qualified keywords all the time too.

❤️ 1
Joshua Suskalo15:10:01

So for clarity, I don't think this is a "we haven't used this feature so we don't know how much we'd like it". Since this can so easily be a macro any time this comes up solutions are offered with a "here, this will work, but I don't use it myself", and I think that's more because the value in it is not seen after use, not so much that it simply isn't used enough to see the value. I know for myself I value the ability to refactor the keys independently of the local variable names more than I value a little less typing. When I'm writing clojure code 80+% of my time is spent thinking, 15% of my time is spent debugging, and 5% of my time is spent writing new code. Having that 5% go down to 4% (probably generous) because I don't have to duplicate some keys sometimes doesn't make much impact to my workflow and creates more gotchyas while I'm doing the debugging.

❤️ 1
seancorfield16:10:10

I would also point to the fact that we have a macro for this at work -- as linked above -- and we have not needed to use it at all! I added the macro to our library because it seemed like a useful, interesting thing... but it just hasn't proved useful in real-world code (and we have over 130K lines of Clojure in a codebase that is over a decade old in places).