clojure-dev 2026-07-08

Following up on Rich's suggestion > Might I suggest the right place and time for that is on the tickets before they are screened/oked? Does that mean, in the context of the closed / merged tickets, that discussion / improvement of these changes is too late and will have to be done in a separate ticket (if at all)? It feels impolite and spammy to trawl tickets for performance issue and nitpick them at that stage These tickets have also been relatively fast tracked.

Alex Miller (Clojure team) 2026-07-08T13:17:37.737419Z

Ask Clojure is the appropriate place for public discussion. Once things move to patches, generally that discussion is happening in the jiras. In this case, the select-keys ticket is 11 years old. I brought it forward when the addition of :select made it likely more common

πŸ‘ 1
Alex Miller (Clojure team) 2026-07-08T13:18:44.147449Z

For closed work, it's best to start new threads on Ask that can turn into tickets if needed

🫑 1
Alex Miller (Clojure team) 2026-07-08T13:19:21.257649Z

Generally we will only go back to closed tickets if there is breakage that causes them to be reverted/reopened

Ack. Trying to be a good citizen here.

Wrt breakage, there might be a slight performance regression in select keys for small keysets, the overhead of transient/persistent! isn't worth it around ~<=2 keys

Alex Miller (Clojure team) 2026-07-08T13:21:16.272059Z

Was tested in the ticket

My non scientific benchmarks gave different results. I'll try to run something more accurate and open a specific ask on the subject if I find something.

I guess the question that should be asked is not "can we make it faster in a benchmark?" but "can we make it faster in real world code?" (even if that makes an edge case slower) How many select-keys calls out there have only one or two keys, compared to three or more?

I guess the counter would be - why not make it faster for all cases? Have I seen select-keys of two keys? Unfortunately, yes πŸ˜„

βž• 1

But adding if / count / <= to switch between transient/non-transient cases would make the transient case slightly slower (just by virtue of adding more code to execute).

dwarfed by the cost of transient/persistent! (probably) The real answer is that you'd need to do a parameter sweep across keyseq and map sizes and JVM versions and find a sweet spot If you wanted to be super rigorous, also across operating systems and architectures

You pay for a similar check every time you grow a PAM. Sometimes it's worth it.

Optimization is a well. https://www.goodreads.com/book/show/11275.The_Wind_Up_Bird_Chronicle. I once did, and will no longer πŸ™‚ And now this thread, on a question about where/how best to interact, has become about optimization. :(

We're still optimizing the process. I'll move further discussion on the subject to #performance

πŸ‘ 1

Following up on the :or destructuring. Also including examples @borkdude :or is fine as it is, but I believe considering another default-providing destructuring form nil-compatible would be really useful. I do understand nil values on maps are an anti-pattern if you write them as literals, but I've seen this too often, even in popular community libs (clj-kondo, Malli, clj-http,...): Preferring

{:a :value
  :b 20
  :c (when smth? :value-2)}
Instead of
(cond-> {:a :value
                  :b 20}
smth? (assoc :c :value-2))
Maybe because it's just shorter. Or even one harder to notice, you create a fn that destructures params, one of those is missing and you pass them through to another:
(defn f [{:keys [a b c]}]
 ;; some logic here to calc x
 (g {:x x
       :c c}))
Then it turns out the caller never passed :c , and you ended up creating a map with nil . I believe maps with nil vals are quite common in Clojure. If it is an anti-pattern, then what is the alternative? doing a some? check for every destructured key before forwarding would look noisy.

Alex Miller (Clojure team) 2026-07-08T16:33:40.493159Z

I think the better way to approach this is via identifying concrete use cases (you have multiple mentioned here I think) and that drives the needs and places where different destructuring capabilities either are or are not delivering

Alex Miller (Clojure team) 2026-07-08T16:34:49.554799Z

https://ask.clojure.org/index.php/13141/or-destructuring-doesnt-update-object?show=13143#a13143 is an example where I've tried to narrow a broader question into a specific use case

πŸ‘ 1

I know we can always merge or assoc the defaults, just wanted to suggest another destructuring expression. I can also see there's already a ticket created. Thanks for linking that!

For your function f, if you want to make sure :c is passed in, use :keys! πŸ™‚ I liked Alex's comment about perhaps maybe introducing a :defaults map in destructuring (if Rich thinks that's a good approach).

:defaultsis highly unlikely as it would a) not be DRY and b) would engender a lot of redundancy with destructuring itself in order to have an equivalent ability to talk about keys when bindings won't match etc. A lot of the work of the checked directives was balancing redundancy and DRY. That said, having the :select map reflect the :or values is still on the table, it just felt a bit gnarly and undecided to include in the first cut. Since :select is new, doing so would be non-breaking and it seems like most here would desire it?

πŸ‘ 5
πŸ‘πŸ» 1

If there was something like :defaults, I suspect many folks would switch their :or uses over to using thatβ€”so it doesn't feel any less DRY than :or but I definitely take your point on that. If modifying :select behavior to reflect :or values is still on the table, I think that would be a great solution to this "ask": you could still get the whole input map with :as and you could get the named keys map with defaults via :select. So, yeah, a big βž•πŸ’― from me. Thank you for considering this.

πŸ‘ 1
βž• 3

:or is clearly downstream of binding<->key mapping and it's not clear whether :defaults would be. It would in any case be yet another very similar feature, and I don't think we need it.

πŸ‘πŸ» 1

I'm happy to see people trying out the new checked keys, & and :select features and thought I might give a backgrounder here to help situate them in the big picture. tl;dr, that picture is a continued focus of Clojure on enabling flexible, information-oriented systems. https://www.youtube.com/watch?v=YR5WdGrpoug (MN) engaged with the fundamental differences between record+field orientation and set-of-keys orientation. And clojure.spec before it embraced RDF's property definitions independent of container membership. Records (generally) have places (fields). Once you have a place, you must have something to go there. That thing could be either: - the intended semantic value - a (universal) placeholder indicating 'no value' (e.g. SQL nulls) - some maybe/optional thingy that is inquire-ably one of the previous two This complects a whole lot of stuff: - the field names and mappings to semantics/types - the set of fields - (maybe) a name for that set - optionality - (maybe, depending on how you represent maybe) the semantic type and the 'missing' type But the set of fields of interest, required or optional, differs by context. Dealing with similar information in a different context requires creating a new record with duplicated information, especially as regards the field semantics and names. RDF defines properties/attributes (things that would be fields) globally, and independent of their presence in any aggregate, collection or container. This is a superior design for information management. It makes property definition and membership orthogonal, and maximizes composability. One might say records were born of a memory orientation and RDF of an information orientation. Clojure is an information oriented language - it supports namespaced keywords, and clojure.spec follows RDF in supporting global definitions of attributes independent of containers, which can then be arbitrarily composed (e.g. via spec/keys). But spec/keys still requires a named spec for each context. That's not so terrible when maps are flat, but becomes a serious problem as things become nested. Every context might need a new set of variant s/keys specs. Consider a map M which can can contain [:a :b :c] where :b is in turn a map (B?) that can contain [:x :y :z]. In some contexts you need an M with a :b/B map that has :x and :y and in others you need an M with a :b/B map that has :y and :z. No single expression can say "B requires (only) :x and :y" and "B requires (only) :y and :z". And what can we say about M's :b then? Do we need a variant of M? The 'schema/select' ideas for spec outlined in Maybe Not reflected the thinking du jour around splitting these things apart, with named, enumerated 'schemas' defining keysets and anonymous 'select' expressions to specify the supported subset(s) in any particular context. But it ends up we already have a way of talking about (anonymous) key subsets - map destructuring. What the checked directives and & do is outfit destructuring with the remaining facilities of MN's 'select'. What :select does is preclude the inevitable request for closed maps :) Whither MN 'schemas/keysets'? It turns out there was a brittleness and closed aspect to their enumerated nature. An alternative is an emergent set, much like namespaces. Let's call them keyspaces, and they would arise from attribute specs sharing the same namespace in their keywords. Once again this is already supported, though you might use it a bit differently to create small 'vocabularies' of keys around a domain (e.g. the classic Person). I won't say more about this now, but it is all of a piece of design work, and not (just) some new nice-to-have mechanisms.

πŸ‘ 16
πŸ‘πŸ» 1
πŸ‘πŸΌ 1
πŸ™ 2
✍️ 8

FYI: We have 1.13.0 Alpha 3 in production for all apps on all servers. I will report back if we see anything unusual (we already had Alpha 2 in production for most apps on some servers and saw no observable changes).