God morgen. While working on deps-deploy
yesterday, I had to do some RTFSing in pomegranate
https://github.com/clj-commons/pomegranate/blob/master/src/main/clojure/cemerick/pomegranate/aether.clj and I'm struck by how different this code is from the code that I write on a day to day basis. From a call-site perspective, kw-args look realy nice:
(aether/deploy :artifact-map artifact-map
:repository repository
:coordinates coordinates)
But reading the code that gets called, not my cup of tea.I like kw-args when you need to do what they call “props drilling” in js react: passing down the same arguments deeply into the callstack and only change part of it.
But I’d rather use a map as an argument which makes everything simpler: (aether/deploy {:a… coordinates})
Yes, I totally see the use case for sending a bunch of stuff in one go, but as you, I prefer doing that in a map rather than with kw-args. Don't remember who's law it is, but basically if your function takes 9 arguments, you probably forgot a couple. Might have been Perlis
What's your preference:
(->> foos (map (fn [foo] [(bar foo) (baz foo)])) (into {}))
or
(reduce (fn [foos foo] (assoc foos (bar foo) (baz foo))) {} foos)
(into {} (map (juxt bar baz)) foos)
I have a slight preference to put into at the front and yes, juxt, thank you plex!
I'm a big into
+ map
+ juxt
fan 🙂
Yeah, didn't see the use for juxt
here as this was a simplification of a more complex example, but still... Maybe worth thinking how this could be solved with juxt
since it probably leads to smaller building blocks
@slipset but there's a law that when you have a chance to use juxt, you should take it. unlike macros
Which law is that? Borkdudes law? 🙂
clj-kondo: warning: this program doesn't use juxt
literally unusable
Is there a way to lint that would suggest juxt, i.e., given the example above?
(I'm still learning when I can use it)
I guess it's something that bikeshed
or kibitz
could do, since they look at patterns
Interesting
So if you can write a pattern that expresses when to use juxt, then you're good.
:thumbsup:
I'm almost always using juxt to turn maps into ordered vectors of the values in maps
And in my example, it gets used to create map entries from vectors so we can stick them in a map
mogge
Oh, I just need to get this off my chest. Wouldn't it be nice if group-by
could get an extra arity so you could avoid doing somehting like:
(->> foos
(group-by :id)
(medley/map-vals whatever))
and rather do
(group-by :id whatever foos)
(somewhat inspired by javas grouping collector (or whatever it's called))
Basically this thing here https://github.com/plumatic/plumbing/blob/master/src/plumbing/core.cljx#L164
🐈
I've just gotten really used to writing
(map (fn [[k v]]
[k (do-sommat-to-v v)])
and just don't use map-vals
grouped-map is a bit different. It looks a bit like by-key from cgrand/xforms
which is perhaps a bit more general as it applies a comped transducer to the groups (does the same for partition)
@slipset You mean (zipmap (map :id foos) (map whatever foos))
⛳
Not if whatever removes :id
@slipset I'm confused. What's an example use-case?
Might not be realistic, but let’s try:
(def users [{:age :best, :name "erik"}, {:age :best, :name "dominic"}, {:age :too-old :name "bruce"])
(grouped-map :age :name users)
=> {:best ["erik", "dominic"], :too-old ["bruce"]}
Oh I see, you want to keep the sequence
@slipset This is just a matter of generalizing group-by right?
(defn group-by2
[f coll f-combine empty f-elt]
(persistent!
(reduce
(fn [ret x]
(let [k (f x)]
(assoc! ret k (f-combine (get ret k empty) (f-elt x)))))
(transient {}) coll)))
#'user/group-by2
user=> (group-by2 :age users conj [] :name)
{:best ["erik" "dominic"], :too-old ["bruce"]}
You could drop the empty
argument and have the combine function defaul accordingly.
(e.g. (fnil conj [])
in this case)
you mean like a 0-arg?
yeah
@ordnungswidrig on second thought no, because you can have nil vals:
user=> (get {:a nil} :a :foo)
nil
good point!
hmm, but ret is our own map
so never mind
true
Otoh you can always thread the result of a simple group-by through a combine function
xforms has this, as @otfrom wisely mentioned :)
(->> x (group-by :some-key) (map (fn [[k vs] (reduce + vs)) (into {}))
(Somebody who knows about transducers can rewrite this to be performant I guess)
((But that’s not the point))
there's also index-by from medley which I sometimes need. which can also be written using the very decomplected group-by2:
(def users [{:age :best, :name "erik"}, {:age :best, :name "dominic"}, {:age :too-old :name "bruce"}])
(defn group-by2
[group-fn combine-fn empty elt-fn coll]
(persistent!
(reduce
(fn [ret x]
(let [k (group-fn x)]
(assoc! ret k (combine-fn (get ret k empty) (elt-fn x)))))
(transient {}) coll)))
(prn (group-by2 :age conj [] :name users))
;;=> {:best ["erik" "dominic"], :too-old ["bruce"]}
(defn index-by [f coll]
(group-by2 f (fn [_ x] x) nil identity coll))
(prn (index-by :id [{:id 1 :foo 2} {:id 2 :foo 2}]))
;;=> {1 {:id 1, :foo 2}, 2 {:id 2, :foo 2}}
(fn [_ x] x)
=> second
😛
not really though?
in lambda calculus it's the expression for false I believe
hmmm
actually true!
Tools namespace, why must you bother me so?
I have half a mind to abandon it
TNS-45 has pestered every clojurescript project I've ever been on. As of yet there's no general purpose solution. I'm very tempted to look at alternatives.