clojure-europe 2026-05-13

good morning

Good morning, fellow Cloj_eu_rians!

good morning, code conjuring folks!

morny-morn!

Good Morning!

good humane morning

πŸ’― 1
🀸 2
🧘 1

What better excuse for a walk than to pick up a parcel containing my new A whistle 😁🎢

Out of curiosity: What's an "A whistle"? "A" like the reference tuning pitch of 440Hz?

I don't know the hertz - tin whistles are an octave or two higher than the reference, I think - but it's tuned to A major, yes 😊

πŸ‘ 1

morning 🎢 just got in from a lovely little walk β›… ☺️

Morning! I'm waiting for the biblical rain here in Manchester to slow a little before starting my walk 🌧️

🀞 1
πŸ˜‚ 3

looks like it's got to this side of the pennines ... #weather-watch

😬 1

good morning folks

Making some breaking changes

πŸ˜‚ 6

ground breaking!!

πŸ˜„ 2

That looks fun!

(transient concrete)

(apply concrete)

You opted for abstraction over concretion

Love the puns πŸ˜„ should be something about breaking the build(ing) as well, but brain capacity is gone. All good fun, except for carrying the stuff outside.

shame that update-vals / update-keys can't take additional arguments

(update-vals m update-in [:foo :bar] inc)
(swap! a update-vals update-in [:foo :bar] inc)
etc

βž• 5

worth an ask.clojure perhaps?

also sharp edge, calling these on a sorted map does not return a sorted map!

😬 2

(update-vals m #(update-in % [:foo :bar] inc)) doesn't seem much pain, compared to trying to change a core function... πŸ˜„

true, but update etc do support it :)

(seriously tho', it might be interesting to see how widespread update-(vals|keys) with an anonymous fn like that is out in the wild?)

I'm mostly curious if it's been considered. My guess is it's intentional, given that core stuff tends to be thought through very thoroughly, but maybe not. I really love the "uniform update model" or whatever they call it, stacking swap!/update/update-in etc

@seancorfield anecdotaly, I count about 5-6 of these in a ~40k lines codebase

Okay, made me curious about our work codebase: we have 20 calls to update-vals or update-keys across 150k lines. The ones that have anon fns are all more complex than the extra args would support. So this enhancement wouldn't help us 😞

@borkdude When I searched, this code in taoensso/encore hooks showed up:

(let [{:keys [src alias attrs body]} (hooks/sexpr alias-node)
                  ;; workaround as can't seem to (get) using a token-node
                  ;; and there's no update-keys (yet) in sci apparently
                  [& {:as node-as-map}] (:children alias-node)
                  {:keys [attrs body]} (zipmap (map hooks/sexpr (keys node-as-map))
                                               (vals node-as-map))]
              (hooks/list-node
               [(hooks/token-node 'taoensso.encore/defalias)
                (or alias src) (hooks/token-node src) attrs body]))
Is that still true of sci?

it is in bb but not in SCI by default since it supports older versions of clojure but maybe I should give up on that and always support the newest

there was a SCI issue about this today

Seems like it could still support older versions of Clojure but have update-keys and update-vals? Or are you concerned about code that defines their own, prior to... 1.11?

I don't intend to copy/paste too many functions, I use most directly from the host clojure

I guess I don't understand enough about sci to understand that comment πŸ™‚

that's basically one giant hash-map with symbols to copied functions from clojure

> also sharp edge, calling these on a sorted map does not return a sorted map! not even update-vals?

user=> (apply sorted-map (range 100))
{0 1, 2 3, 4 5, 6 7, 8 9, 10 11, 12 13, 14 15, 16 17, 18 19, 20 21, 22 23, 24 25, 26 27, 28 29, 30 31, 32 33, 34 35, 36 37, 38 39, 40 41, 42 43, 44 45, 46 47, 48 49, 50 51, 52 53, 54 55, 56 57, 58 59, 60 61, 62 63, 64 65, 66 67, 68 69, 70 71, 72 73, 74 75, 76 77, 78 79, 80 81, 82 83, 84 85, 86 87, 88 89, 90 91, 92 93, 94 95, 96 97, 98 99}
user=> (update-vals *1 inc)
{0 2, 70 72, 62 64, 74 76, 86 88, 20 22, 72 74, 58 60, 60 62, 24 26, 88 90, 46 48, 4 6, 54 56, 92 94, 48 50, 50 52, 32 34, 40 42, 56 58, 22 24, 90 92, 36 38, 44 46, 6 8, 28 30, 64 66, 34 36, 12 14, 2 4, 66 68, 82 84, 76 78, 68 70, 14 16, 78 80, 26 28, 16 18, 38 40, 98 100, 30 32, 96 98, 10 12, 18 20, 52 54, 42 44, 80 82, 94 96, 8 10, 84 86}
user=> (type *1)
clojure.lang.PersistentHashMap

(same with update-keys, which should be no surprise)

Source:

(with-meta
    (persistent!
     (reduce-kv (fn [acc k v] (assoc! acc k (f v)))
                (if (instance? clojure.lang.IEditableCollection m)
                  (transient m)
                  (transient {}))
                m))
    (meta m)))

I think {} would need to be (empty m) for that to be retained?

I thought they had used empty in update vals

I recall asking Alex about it years ago

Maybe there is no transient sorted map?

What does (into (sorted-map) (range ...)) return

Seems likely:

user=> (apply sorted-map (range 100))
{0 1, 2 3, 4 5, 6 7, 8 9, 10 11, 12 13, 14 15, 16 17, 18 19, 20 21, 22 23, 24 25, 26 27, 28 29, 30 31, 32 33, 34 35, 36 37, 38 39, 40 41, 42 43, 44 45, 46 47, 48 49, 50 51, 52 53, 54 55, 56 57, 58 59, 60 61, 62 63, 64 65, 66 67, 68 69, 70 71, 72 73, 74 75, 76 77, 78 79, 80 81, 82 83, 84 85, 86 87, 88 89, 90 91, 92 93, 94 95, 96 97, 98 99}
user=> (transient *1)
Expected IEditableCollection, but was given PersistentTreeMap, at user/eval18728 (REPL:1) - runtime error (unexpected type).
user=> (apply sorted-map (range 100))
{0 1, 2 3, 4 5, 6 7, 8 9, 10 11, 12 13, 14 15, 16 17, 18 19, 20 21, 22 23, 24 25, 26 27, 28 29, 30 31, 32 33, 34 35, 36 37, 38 39, 40 41, 42 43, 44 45, 46 47, 48 49, 50 51, 52 53, 54 55, 56 57, 58 59, 60 61, 62 63, 64 65, 66 67, 68 69, 70 71, 72 73, 74 75, 76 77, 78 79, 80 81, 82 83, 84 85, 86 87, 88 89, 90 91, 92 93, 94 95, 96 97, 98 99}
user=> (transient (empty *1))
Expected IEditableCollection, but was given PersistentTreeMap, at user/eval18732 (REPL:1) - runtime error (unexpected type).
(error messages courtesy of rephrase πŸ™‚ )

Not at the computer

user=> (into (sorted-map) (partitionv 2 (range 50)))
{0 1, 2 3, 4 5, 6 7, 8 9, 10 11, 12 13, 14 15, 16 17, 18 19, 20 21, 22 23, 24 25, 26 27, 28 29, 30 31, 32 33, 34 35, 36 37, 38 39, 40 41, 42 43, 44 45, 46 47, 48 49}
user=> (type *1)
clojure.lang.PersistentTreeMap

so sorted-map and sorted-set aren't IEditableCollections

The impl of update-keys and update-vals are subtly different: update-keys always uses (transient {}) whereas update-vals will use (transient m) if it is editable.

yeah, that's why it's tangential

but them sorted collections not being editable makes me want to reconsider using them as first args to into vs sorting after into {}

Apparently, we don't use sorted-set or sorted-map anywhere in our code at work...

I made an https://ask.clojure.org/index.php/12107/update-keys-and-update-vals-missing-the-args-from-update?show=12107#q12107 a while back, I think it's not a big deal, but it bugs me a bit that I can't chain them like the other update fns in core. Consider voting for it if you agree πŸ™‚

It also bugs me. However, looking at our codebase of the 5 update-* with anon functions, only 2 would've benefitted from this arity. In @seancorfield’s codebase apparently it was 0 so I personally don't feel the urgency 🫠

I voted for the Ask years ago, but I also upvoted Alex's answer about why they are not variadic πŸ™‚

Β―\(ツ)/Β―

Please do feel free to add more upvotes on the question in ask or more comments with specific use cases.

Both of those help raise the priority.

sorted-map and sorted-set are great, I've really come to appreciate them on our current project. Together with subseq/rsubseq they become powerful in-memory indexes that you can scan from any point in either direction. Great for time series data for instance and for any kind of range queries. The ones in core don't have transients, there's a library that does have them and is otherwise compatible https://github.com/clojure/data.avl

Thanks @plexus, I'm starring that lib

I wonder why core sorted map and set don't support transients though