clojure-dev 2026-07-14

Under what circumstances does +' promote doubles? For instance, (+' Double/MAX_VALUE Double/MAX_VALUE) just returns ##Inf, which is the same thing that + does. That seems like it’s giving up too quickly. Does +' really just work with longs? The doc string says Returns the sum of nums. (+') returns 0. Supports arbitrary precision.

From one of the links in Alex' answer: > If automatic promotion to a BigInt is required, and introducing a BigInt to the original operation to make use of BigInt contagion is not possible, Clojure provides a set of alternative math operators suffixed with an apostrophe: +', -', *', inc', and dec'. These operators auto-promote to BigInt upon overflow, but are less efficient than the regular math operators in that they cannot return primitive integers, only boxed ones. β€’ https://web.archive.org/web/20120706124330/https://dev.clojure.org/display/doc/Documentation+for+1.3+Numerics

The rationale for auto-promotion only working with integers is also given in a comment from Alex: > Per Rich - promotion is an integer-only concept, concerning different available scales between long and bigint and that is what is intended for all promoting operators. Double and BigDecimal both have huge scales, they are more about differences in precision and promotion does not (should not) apply in those circumstances.

OK, fair. But if that’s the case then the doc string should probably be updated to clarify that it really only applies to integer arithmetic, not floating point.

I’ll add a comment to http://ask.clojure.org. Thanks for the link.

given that https://clojure.atlassian.net/browse/CLJ-1078 is looking good for inclusion, would there be interest in adding transient support for queues?

Alex Miller (Clojure team) 2026-07-14T13:58:20.206179Z

I'm not sure how that would be done, but does not seem that useful

i have no idea about implementation lol, the transient stuff is beyond my knowledge

First off, thank you for making the Alpha 3 syntax a compile error so it's easier to find and fix & uses to adapt to the Alpha 4 syntax: > Idents after & in :keys!/syms!/strs!/:keys/syms/strs must now be actual keys, not binding symbols. This is a change in syntax since alpha3. Note that symbol keys should be quoted as unadorned symbols are binding symbols. Could you speak to the thinking that led to this change, where we would now have {:keys [a & :b]} with a mix of symbols and keys?

The identifiers before the & are bindings, those after are not. :keys et al exist not for the mechanical assist of looping off a :, but to alleviate having to write {foo :foo bar :bar baz :baz} Supporting that transformation after & would be for mechanical effect only. People are confused enough about when binding occurs, keeping unadorned symbols for references to bindings is an attempt to keep things clear.

πŸ™ 2

Cool, thanks. I suspected that was the intent but just wanted to confirm I wasn't missing something.

Thanks for the new alpha. I'm trying to understand the reasoning of Rich, but I don't fully get it and I feel stupid πŸ˜… So just my 2 cents, I find it a bit confusing that the keywords behind & don't get the namespace. From my new/naive view the two examples below should have the same outcome, but they don't due to the prefix.

def person {:person/name "a" :foo "b"})

(let [{:person/keys! [name & :foo]} person] name) ;;=> "a"
(let [{:person/keys! [name foo]} person] name) ;;=> "Missing required key: :person/foo"

πŸ‘ 2
Alex Miller (Clojure team) 2026-07-14T19:38:43.307099Z

I'll wait to see what Rich says, makes some sense to me.

The ampersand could be dropped then? Say: {:keys [:a :b c]} C is bound since I declared it as a binding.

Alex Miller (Clojure team) 2026-07-14T19:53:25.105709Z

the ampersand has meaning - things before are bound, things after are not

Alex Miller (Clojure team) 2026-07-14T19:54:54.354779Z

bindings are allowed to be unbound symbols, keywords, or qualified keywords and unbound can be anything so there is no way to partition based on type, this is not up for debate

unbound can be symbols strings or keywords, not anything

Oh, can you have keywords before the ampersand now and they get bound to a symbol?

always could

🀯 1

esp to support ns aliases

=> (let [{:keys [:a]} {:a 10}] a)
10
Holy smoke, never knew that haha. It makes this change a lot more logical then.

from my reading, the discrepancy @jeroenvandijk brought up seems to be a bug cuz otherwise we can't actually :keys! check namespaced keywords

(This code was run on Alpha 3 by accident)

user=> (def person {:person/name "a" :foo "b"})
#'user/person
user=> (let [{:person/keys! [name foo]} person] name)
Execution error (IllegalArgumentException) at user/eval124 (REPL:1).
Missing required key: :person/foo
user=> (let [{:person/keys! [name & :foo]} person] name)
Execution error (IllegalArgumentException) at user/eval128 (REPL:1).
Missing required key: :person/foo
user=> (let [{:person/keys! [name & :person/foo]} person] name)
Execution error (IllegalArgumentException) at user/eval132 (REPL:1).
Missing required key: :person/foo
It should produce this on Alpha 4:
(!2026)-> clj
Clojure 1.13.0-alpha4
user=> (def person {:person/name "a" :foo "b"})
#'user/person
user=> (let [{:person/keys! [name foo]} person] name)
Execution error (IllegalArgumentException) at user/eval119 (REPL:1).
Missing required key: :person/foo
user=> (let [{:person/keys! [name & :foo]} person] name)
"a"
user=> (let [{:person/keys! [name & :person/foo]} person] name)
Execution error (IllegalArgumentException) at user/eval127 (REPL:1).
Missing required key: :person/foo
user=>

to be clear there's no discrepancy as this is a new space and the semantics are being defined in this alpha phase

πŸ‘ 1

oh hm, interesting result, sean

we actually support a heterogeneous set of keys after & (so you don't need to use several different directives just to address them)

@jeroenvandijk Can you confirm your observation? I can't repro...

given that, there's an argument for WYSIWYG there

@seancorfield yeah i still get this

(def person {:person/name "a" :foo "b"}) 
(let [{:person/keys! [name & :foo]} person] name) ;=> "a"
Let me see if there is something off with how I'm trying via deps-try org.clojure/clojure "1.13.0-alpha4"

while tedious, it's better if examples place everything inline vs a def somewhere above

πŸ‘ 1

@seancorfield I get

user=> (let [{:person/keys! [name & :foo]} {:person/name "a" :foo "b"}] name)
"a"

βž• 1

says exception above

i.e. this is WYSIWYG

that would be surprising to me, as everywhere else the prefix affects all listed keys

no conveyance of :ns/keys 's ns to keys

It actually does not

Hah... I didn't force a recompute of the classpath in the project I ran a REPL and didn't notice it was still Alpha 3. Apologies.

it only affects bindings and explicit keys as bindings are disallowed by the specs

user=> (let [{:person/keys [:name]} {:name "foo"}] name)
Syntax error macroexpanding clojure.core/let at (REPL:1:1).

Confirmed now:

(!2023)-> clj -M:1.13
Clojure 1.13.0-alpha4
user=> (def person {:person/name "a" :foo "b"})
#'user/person
user=> (let [{:person/keys! [name & :foo]} person] name) ;=> "a"
"a"

πŸ‘ 1

right now, the magic of how the directives coerce a binding name to a key is limited to binding names

Except before the ampersand it conveys to keywords as well:

=>(let [{:person/keys [:a]} {:a 10}] a)
nil
=>(let [{:person/keys [:a]} {:person/a 10}] a)
10

user=> (let [{:person/keys [:a]} {:person/a 10}] a)
Syntax error macroexpanding clojure.core/let at (REPL:1:1)

i mean that the simple symbol binding is matched to the namespaced keyword, and i would anticipate that this applies to the checked but not bound keys as well. if it doesn't, {:keys! [a b & :c] :person/keys! [d e & :f]} means that :f could be in either and imo is ambiguous until you learn the behavior

@didibus on Alpha 4:

(!2027)-> clj
Clojure 1.13.0-alpha4
user=> (let [{:person/keys [:a]} {:a 10}] a)
Syntax error macroexpanding clojure.core/let at (REPL:1:1).
:person/keys - failed: simple-symbol? at: [:bindings :form :map-destructure :map-binding 0 :local-symbol] spec: :clojure.core.specs.alpha/local-name
...

> (let [{:person/keys [:a]} {:person/a 10}] a) Doesn't work on 1.12.5. But it does in cljs πŸ™‚

this was an error on prior releases as well

given the existing behavior of :keys and :person/keys, i would expect that {:person/keys! [d e & :f]} would work the same way: grab :person/d, :person/e, and check :person/f

@nbtheduke "applies to the checked but not bound keys" we probably need to stop thinking of them as "not bound keys": in earlier alphas, yes, they were symbols that were not bound, but in alpha 4 they're no longer "symbols for binding", they're actual keys instead.

seems beneficial for understanding code to reduce ambiguity and keep things allowed in one place only (simple keys are bound or checked in :keys!, qualified keys are bound or checked in :ns/keys!)

@nbtheduke everyone has different expectations and they can't all be met. Sometimes the details must just be learned if not intuited. However, we are guided by trying to give semantics to the thing and not just consider it a mechanical contraption πŸ™‚

so we have at present:

naturally, and i am only offering my opinion here on what i as a long-term clojure user would expect given my previous experience

βž• 2

Oh doh, this is my fault for being lazy and trying it on https://tryclojure.org/

a) no behavior of what happens to keywords as binding names in :ns/keys from prior releases since they were (and still are) disallowed.

b) a desire to support heterogeneous sets of keys after & so as to reduce the need to a lot of directives

the tradeoffs are: a) with WYSIWYG you might have to repeat :ns prefixes. b) with magic prefixing you can't refer to unqualified keys, need a separate directive

πŸ‘ 1

@nbtheduke and everyone tries to bolster their own expectations by saying they'd be shared by everyone else πŸ™‚ Seriously it's the least persuasive way to convince me

But could just do :keys [& :foo/a :bar/b] if wanting b), no?

☝️ 1

i'm offering my opinion alone and i think i've done a good job of only speaking for myself

mostly kidding, it's fine to have an opinion but I'm inviting everyone to play the semantics/compatibility/tradeoffs game instead of the expectations game

πŸ‘ 2

yeah, i appreciate you stating the tradeoffs

βž• 1

I will also say as a general principle less magic == better

We started with propagating magic post & and it gets thick quickly

it's much more straightforward to confine the magic to the mappings from binding-names->keys, since that's what the feature is there for

you have to do something get from an unadorned symbol to a key, you don't to get from a key to a key, just use the key you want

I agree about the magic. I haven't used the prefix keys much. What do you think about this one?

(let [{:person/keys! [foo/a]} {:person/a 10}] a) ;;=> 10 ; Hint: the foo prefix is ignored

The only downside I can see is if you want :ns/keys! binding only a few items but still want it to check many other (qualified) keys are present. It'll mean a lot of :ns/ prefixes. But I suspect that will be pretty rare in real world code.

Alex Miller (Clojure team) 2026-07-14T20:33:48.603689Z

I think I'd consider that undefined behavior that the spec is just not refined enough to catch right now (yet)

πŸ‘ 1
Alex Miller (Clojure team) 2026-07-14T20:35:02.452909Z

@jeroenvandijk I get an error with that - what are you running?

Perhaps consider that a regression from 1.12 since (with :keys, not :keys!):

(!2009)-> clj -M:1.12
Clojure 1.12.5
user=> (let [{:person/keys [foo/a]} {:person/a 10}] a)
Syntax error macroexpanding clojure.core/let at (REPL:1:1).
:person/keys - failed: simple-symbol? at: [:bindings :form :map-destructure :map-binding 0 :local-symbol] spec: :clojure.core.specs.alpha/local-name
...

@alexmiller 1.13.0-alpha4 via deps-try on java 25. I can check elsewhere to be sure

Alex Miller (Clojure team) 2026-07-14T20:36:27.997979Z

ah, yeah this was an error previously, I may have inadvertently loosened that part of the spec too much in 1.13

let's please leave the ! off :keys in this discussion since required-ness is not relevant

πŸ‘ 1
πŸ‘πŸ» 1
Alex Miller (Clojure team) 2026-07-14T20:40:52.651749Z

yeah, this is an unintended bug in the spec change, I will fix that for next alpha.

πŸ‘ 1

maybe it's helpful to read the "&" as "oh and while you're here check for the presence of these keys: ...". That works for me anyway

When doing {:person/keys [before & :after]} before the ampersand you can only have simple-symbols. But when doing {:keys [before & :after]} you are allowed before to be keywords and namespaced qualified keywords. It means when you do :person/keys you cannot bind anything that's using a different namespace qualifier. As a 3rd option, I think it could make sense to keep that restriction in after as well, so you are only allowed unqualified keywords and they'd get automatically namespaced with :person same as before bindings.

πŸ‘ 1

@jjttjj that is the semantic - the feature is checked-keys destructuring

@didibus "before the ampersand you can only have simple-symbols." is a breaking change

oh only for :ns/keys

1

I hate example based communication πŸ™‚

it's always difficult to see what part of an example is exemplifying to the author

if they don't make it explicit

@didibus Sounds like you'd prefer to not have Rich's b) option above and instead have multiple directives, i.e., both :ns/keys and :keys ?

i would prefer that, yes. i don't mind multiple directives

βž• 1

I am strongly disinclined to have some directives behave one way and others another

this is just treating the thing like a magic machine

There's magic from the point of view of implementation vs. usage. I'd argue for usage what's familiar/expected = less magic. Also, more uniformity = less magic. If the given list of tradeoffs is comprehensive, I'd prefer we go back to just using & to denote "not binding" (why do we need both & and another special 'rule') and keep existing rules (more uniform). So :person/keys [foo & address/bar] shouldn't be allowed (not allowed currently without & if I'm not mistaken). If wanting concision, use :keys [person/foo & address/bar].

I'm not sure what I prefer haha, haven't hammocked enough. But I was noticing there's another pattern that could also have a logical consistency behind it. Which is that when you use :ns/keys the spec now restricts your bindings to be simple-symbols. So it could be consistent that it would also restrict the & after declared keys to be simple-keywords.

The WYSIWYG aspect is very compelling to me, i.e., any keys following & are used exactly as-is. I may later request an enhancement if I find myself writing lots of qualified keywords after & at some future point (but based on our current code, that's very unlikely I think).

βž• 1

right now the directives have semantics related to getting from a binding name to a key, tied to their primary mission, which is neither : elision nor ns-qualification, but rather key repetition avoidance, e.g. {foo :foo, bar :bar, baz :baz}

keys have no such need

WYSIWYG seems to point to the opposite interpretation, at least to me: I'm in a :ns/keys context, specifying a vector of keys to look for. I would assume that ns prefix applies to everything that follows

πŸ‘ 3

The WYSIWYG aspect is very compelling to me, i.e., any keys following & are used exactly as-is.
Well, they're not really "used" in a sense. :keys! does check, true. To me it's not clear neither what I see nor what I get with :person/keys [& :address/bar] πŸ™‚ A more compelling argument to me than WYSIWYG is that symbols usually mean "local binding", so :keys [a & :b] makes what is bound super clear at a glance. Same reason why :keys [:a] is probably less used. What is the argument for why (let [{:person/keys [address/bar]} {}] bar) is not allowed currently in non-alpha?

I'm probably reacting more to how surprising :ns/keys behave.

user=> (let [{:user/keys [name email wallet/amount]}
  #_=>       #:user{:name "foo"
  #_=>              :email "foo@gmail.com"
  #_=>              :wallet/amount 123.34}]
  #_=>   [name email amount])
["foo" "foo@gmail.com" nil]

user=> (let [{:user/keys [name email :wallet/amount]}
  #_=>       #:user{:name "foo"
  #_=>              :email "foo@gmail.com"
  #_=>              :wallet/amount 123.34}]
  #_=>   [name email amount])
             clojure.lang.ExceptionInfo: Call to clojure.core/let did not conform to spec.
clojure.lang.Compiler$CompilerException: Syntax error macroexpanding clojure.core/let at (1:1).
Since it doesn't apply like a default namespace, the way that the #:user{} syntax does. There's no way to mix and match and bind keys from other namespaces or unqualified. P.S.: Would be nice if the spec rejected: {:user/keys [name email wallet/amount]} because the latter is very confusing. `

Would be nice if the spec rejected: {:user/keys [name email wallet/amount]} because the latter is very confusing.
@didibus It is rejected on 1.12.5 for me. (Again, not in cljs.)

user=> (let [{:user/keys [name email wallet/amount]}
  #_=>       #:user{:name "foo"
  #_=>              :email ""
  #_=>              :wallet/amount 123.34}]
  #_=>   [name email amount])
["foo" "" nil]
user=> (clojure-version)
"1.13.0-alpha4"

I think Alex's comment above indicates that it is supposed to be rejected in 1.13 as well but the spec was loosened.

πŸ‘ 1

In other words, what you're trying has always been illegal before and is "just" undefined behavior right now πŸ™‚

@mithrandir03 I meant "used exactly as-is" for checking yes... > Well, they're not really "used" in a sense. :keys! does check, true.

πŸ‘ 1

Something like this can definitely get a bit confusing:

user=> (let [{:user/keys [name email & :wallet/currency]
  #_=>        :wallet/keys [amount]}
  #_=>       #:user{:name "foo"
  #_=>              :email ""
  #_=>              :wallet/amount 123.34}]
  #_=>   [name email amount])
["foo" "" 123.34]
Because of the uneven application of the conveyance of :ns/keys and also it's limitation in letting you mix and match within the same definition.

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

I'm not sure what you think is confusing here. As Rich said, examples aren't very compelling unless they're clearly annotated with the point you're trying to make.

& :wallet/currency in :keys is purely for documentation: "Oh, by the way, I also accept the key :wallet/currency"

But you're in a vector of :ns/keys

βž• 2

The confusion is that you are forced to split into two keys declaration for the mixed binding destructuring, but than it's confusing where the & after go since they can go anywhere.

Why no ns (or different ns) for that one?

"it's on the other side of the &" feels a little arbitrary

As Rich or Alex noted, & has a specific semantic meaning here: what comes before and what comes after are two very different things.

Alternatively, you have to go back to the keyword syntax:

user=> (let [{:keys [:user/name :user/email :wallet/amount & :wallet/currency]}
  #_=>       #:user{:name "foo"
  #_=>              :email "foo@gmail.com"
  #_=>              :wallet/amount 123.34}]
  #_=>   [name email amount])
["foo" "foo@gmail.com" 123.34]
But now it's unclear again what is bound and unbound.

Sure, but it's visually bundled in the same vector that has :ns/keys in front of it

βž• 3

@didibus Yes, the keys after & could go in either :*/keys vector. Or in a separate :keys [& ...] directive.

Two different things, but visually they're grouped together

@didibus This would work, no?

:keys [user/name user/email wallet/amount & :wallet/currency]}
;; or
:user/keys [name email], :wallet/keys [amount & :wallet/currency]
Having to add the ns to that last & :wallet/currency is kind of awkward though.

πŸ‘ 1

@didibus Your example works on both 1.12.5 and 1.13 BTW (on 1.12 because & is treated as a binding symbol) πŸ™‚

@mithrandir03 or...

:user/keys [name email], :wallet/keys [amount], :keys [& :wallet/currency]

Since you have multiple directives anyway, I think this is clearer.

It's not ideal that all :wallet/* keys are not grouped xD

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

Oh right, I forgot namespaced symbols can be accepted too on the binding. Ya, it all just is a bit confusing to remember.

There's a lot of rules. Before & it accepts unquoted symbols both qualified or not, as well as keywords. After & it accepts keywords, strings or quoted symbols even if you are inside :keys. But if you are inside :person/keys than before & it no longer accepts keywords or qualified symbols and the unqualified unquoted symbols will get the namespace, but not after &.

πŸ‘ 1

This is a bit strange as well:

user=> (let [{:syms! [& "b"] :as m} {:a 1}] m)
java.lang.IllegalArgumentException: Missing required key: "b"

πŸ‘ 1

> if you are inside :person/keys than before & it no longer accepts keywords or qualified symbols Not "no longer". 1.12 does not accept those either.

There's a lot of rules but I have always used a lot of qualified keys with destructuring and it has always felt really natural in practice and I never have had to think about all these edge cases, and I'm pretty sure the new & stuff will also feel natural once we start using it. for me what made it click with & is to think about primarily using :keys with no qualifier, that's the headline feature, which now supports &, which means "AND by the way while you're here, check for these keys:". it just so happens to work with :ns/keys too, which you always have the option to use if it makes the most sense in that spot.

user=> (let [{:foo/keys [:k]} {}])
Syntax error: :foo/keys - failed: simple-symbol? at: [:bindings :form :map-destructure :map-binding 0 :local-symbol] spec: :clojure.core.specs.alpha/local-name
...
user=> (let [{:foo/keys [a/k]} {}])
Syntax error: :foo/keys - failed: simple-symbol? at: [:bindings :form :map-destructure :map-binding 0 :local-symbol] spec: :clojure.core.specs.alpha/local-name
...
user=> *clojure-version*
{:major 1, :minor 12, :incremental 5, :qualifier nil}

@didibus Again, an example without explaining what/why is "a bit strange"...

Looking at that code, I'd wonder why you used :syms! instead of :strs! there since the only key you mention is a string.

Right, I don't mean this change specifically. I mean the total set of rules around destructuring. So on top of the existing rules you already had to remember, there are now even more. Right, sorry. It's strange that what comes after & does not care if you used :keys, :strs. or :syms at all.

They're all "keys". Not keywords. Just keys.

So you want them accepted by whatever of :keys / :strs / :syms you would be using.

it's just a new type of "operator" in this mini destructuring "dsl".

Ya, I'm getting the feeling that everything after & has now become unrelated to the :keys :syms and :strs directive. In alpha3 it still made sense inside them because it leveraged them to know what the unquoted symbol was representing. Now in alpha4, it creates ambiguity I think. We see that none of the features associated with :keys, :syms and :strs work after the &. You can't use the :ns/keys feature to omit repeating the namespace, and you can't use them to indicate the type of key.

πŸ‘ 1

It makes me wonder if this would be better:

{:keys! [a b] :keys [c] :extras! [:e "f" 'g] :extra [:h]}
Or if you should allow unquoted symbols as well after the & which would behave similar to unquoted symbol before the & minus the binding. So that it makes sense again why it is included inside the :keys vector. I hold no strong opinions here, just thinking out loud a bit.

you could never use anything to omit the namespace in clojure.spec/keys and it was never a big deal. and this is just adding the ability to tack on that type of thing to your normal destructuring (which you dont have to change)

πŸ‘πŸ» 1

@jjttjj I agree that in the common case you only use :keys and :keys! 99% of the time, and won't worry too much and now the only "quirk" or maybe "clarity" (depending on how you see it) is that after & you can only use keywords and before you can use either keywords or symbols. But in the other 1%, it does seem you have to pull up the REPL. Or alternatively, I'm trying to figure out the easy to remember rule. Which I think here is that, everything after ampersand has nothing to do with :keys or :syms . It's like an extra definition that so happen to be allowed to show up in all of them.

> after & you can only use keywords Or strings or (quoted) symbols. Because they're just keys.

sure but the names are separate per type, :keys, :syms, :strs

Not after &. They're just keys.

Before &, yes, they are bindings, per the type of the directive.

Bindings before &, keys afterward:

user=> (let [{:strs! [foo & :foo]} {:foo 1 "foo" 2}] foo)
2
user=> (let [{:strs! [foo & :foo]} {:xfoo 1 "foo" 2}] foo)
Execution error (IllegalArgumentException) at user/eval190 (REPL:1).
Missing required key: :foo
user=> (let [{:strs! [foo & :foo]} {:foo 1 "xfoo" 2}] foo)
Execution error (IllegalArgumentException) at user/eval194 (REPL:1).
Missing required key: "foo"

oh yeah i understand that, i just think that's confusing and unnecessary and breaks with existing convention

& is a brand new feature with its own semantics. I'm not sure what "existing convention" you have in mind. As I noted in another thread here, I have not found it confusing/surprising when adopting the features in real world code at work.

(I think it's perhaps unfortunate that people formed expectations based on Alpha 3 that are no longer valid for Alpha 4 πŸ˜‰ )

That speaks to the problems with alpha 3 :)

🀣 1

Maybe I liked alpha 3 better πŸ˜›

βž• 1

But I'll get used to wtv and move on with my life

πŸ‘ 1

My summary πŸ™‚ Arguments for alpha4 changes: 1. Clearer visual and semantic separation of what is bound vs unbound. 2. WYSIWYG. 3. Support a heterogeneous set of keys after &. My arguments against: 1. I like this as I've said previously. But: a. & is enough of a signal, do we need another? b. Benefit doesn't apply for valid case :keys [:a & :b]. 2. We lose the ability to say :foo/keys [a & b] and must instead do :foo/keys [a & :foo/b] or :foo/keys [a], :keys [& :foo/b] etc. Wrong tradeoff. 3. Laudable, but: a. We already live with this not being supported for :foo/keys [...]. So breaks existing conventions. b. Now, in addition to the semantic difference across &, there's also difference in behaviour wrt to namespaces. But this difference is syntactically grouped within same :foo/keys [... & ...] directive. Confusing. Wrong tradeoff. c. How actually useful is it to be able to say :foo/keys [a & :bar/b]? Much more likely people would prefer to write that as :foo/keys [a], :bar/keys [& b] (etc.).

βž• 2

:foo/keys [a], :keys [& :bar/b] would be my choice for 3c πŸ™‚

Yes, didn't want to write all alternatives. Point being: you wouldn't use the heterogenous form πŸ˜‰

There is a hard semantic difference before and after & no matter what color you paint the bike shed - before are bindings and after are not.

I have a little bit of sympathy for 2. but as I said, I'd want to see how this pans out in real world code before deciding whether some new shorthand would be worth the added language complexityβ€”how many non-bound qualified keys are people really going to have in their code? For non-`!` cases, it's purely documentation. For ! cases, so far at work I've only needed a few such checked (non-bound) keys, so the burden of repeating the ns qualifier is minimal.

Making non bindings look like bindings is an antipattern given the misconceptions they engendered for the moment they were available.

Yes, that is one compelling argument. Have you considered :foo/keys [a & :b] (referring to :foo/a and :foo/b)? It's kind of strange, sure. But perhaps strikes the balance?

@seancorfield features do not exist in the void, they exist in a context. that context informs expectations by readers/users. the current context is that you can destructure multiple homogeneous keys at a time with one of three special forms, :syms for symbol keys, :strs for string keys, and :keys for keyword keys.

πŸ‘πŸ» 1

the type of the expected key is communicated by the special keyword, because you (generally) use symbols in the binding vector

What if & was just it's own entry: {:keys [a b] :& [:c]} I guess it's a bit more typing, so is it worth it to make the edge cases clearer, not sure, but as others have said, maybe & isn't practically going to be used that often either.

allowing heterogeneous types in the binding vector (behind a new special symbol) breaks with that context, changes the assumptions one can make about the type of key that is being checked

πŸ‘πŸ» 1

I am unconvinced by these hard expectations and assumptions regarding a new feature, context (&) and semantic (not bindings).

i think the special symbol & and checking keys is great, to be clear. i like it a lot. i would prefer that :keys only checks keyword keys, :strs only checks string keys, and :syms only checks symbol keys

But the horse is dead now :) Your opinions are noted.

πŸ‘ 1
πŸ˜‚ 1
πŸŽ‰ 1

Have you considered :foo/keys [a & :b] (referring to :foo/a and :foo/b)? It's kind of strange, sure. But perhaps strikes the balance?
> That breaks heterogenous keys (no way to refer to plain keys)

Sure, but I'd take that tradeoff πŸ™‚ It wouldn't break :keys [& :foo/a :bar/b :baz]. Being able to say :foo/keys [a & :bar/b :baz] seems not that useful to me, but I repeat myself. I'm also really excited by the new additions! And with the fact that you (Rich) are more active here. I would interpret the amount of feedback as reflective of enthusiasm and love of Clojure rather than complaining/dissatisfaction πŸ™‚

I do, and I appreciate the feedback, but your points have been made.

Same, this entire feature has me very excited! One question out of curiosity, you said: > Making non bindings look like bindings is an antipattern given the misconceptions they engendered for the moment they were available. Do you mean here that keywords before the ampersand, as currently supported, was itself a bit of a mistake in retrospect? As it makes non-bindings look like them? Or you were referring just to alpha3 making things after the ampersand look like binding?

The latter

πŸ‘ 1

Late to an already too busy thread - and choices seem to have already been made. Either way: 1) To me the directive :& (mentioned above) or just calling it something like :reqs with heterogenous WYSIWYG keywords/symbols/strings would avoid all these semantic collisions. 2) Short of that, not supporting & in ns qualified directives would also be an improvement to at least avoid that class of confusion. 3) I'm also quite curious how :syms usages will look for edge cases since in the symbol type keys case everything looks like a "binding symbol" visually.

for 3), the alpha4 requirement is that & symbols need to be quoted: [a b & 'c]

1
βœ”οΈ 1

the support for key->v in addition to binding-name->v in :orfollows

Can you give a use case for :defaults (now that I've established it seems to include the entire :or map, regardless of which keys were or were not provided)? Is it to provide a way to (merge dfts input) without having to duplicate the default map in both places?

Yes, it's to avoid repetition should they be of interest later in the body. When nested maps are in play, the new nesting/defaults support in :select is a winner for most use cases, else you are tree walking

πŸ‘πŸ» 1

I have our code at work switched from Alpha 3 to Alpha 4 and I'm running our test suite. I'll start looking for places where :defaults would help us eliminate duplicate next.

Alpha 4 will be on our QA cluster shortly for more in-depth testing, with a view to promotion to production in a day or two. I did not find anywhere in our code where :defaults would be usefulβ€”but I suspect that's because we've structured the code differently due to not wanting to duplicate the :or map, and I just haven't spotted the patterns we used for that πŸ€”

How would one declare not-to-be-bound keys when not using the shorthand syntax? Like if I want:

(let [{:keys! [a & :b]} {:a 1 :b 2}] a)
But written:
(let [{a :a _b :b} {:a 1 :b 2}] a)

Not-to-be-bound but required? How would you get the required part without :keys!?

I guess I have that question too! Does it mean that shorthand is no longer just shorthand, but it's become a more powerful expression and no longer just syntax sugar over the long form?

I guess in the verbose way you have no syntax of telling that a key is required

you could use (req! ) afterwards though on the :as or :select ;)

βž• 1

if you want more verbosity even ;)

Well, the shortform has always had a name collision issue. Which forced you to use the long form, like say for the map {:person/name :dog/name}. So I was curious if there was a way to still use the long form

We have a few places at work that are a mix of :keys! and the long form πŸ™‚

(defn- service-start
  [{:keys! [worldsingles-application members actioner database]
    {:keys! [& :redis-pubsub] pubsub :redis-pubsub} :worldsingles-application
    :as service}]
  ...)

Alex Miller (Clojure team) 2026-07-14T21:43:20.309999Z

the ! forms are the only way to get required checking. the & is the only path to unbound key specification

That checks the key :redis-pubsub is present and uses long form to bind it to pubsub, all as a submap of :worldsingles-application which is required at the top level.

Here's another example from work:

(defn daily-member-unread-notifications-step!
  [{:keys! [& :stop :conversations-chan]
    :keys [notification-interval
           wants-daily-notifications?
           member-is-present?
           timeout
           &
           :notification-pairs
           :unread-and-not-notified
           :last-notification
           :record-notification
           :now!]
    :as cap} daily-notification-state val _chan]
  ...)

We used to just put up with the "unused binding" warnings from the linter πŸ™‚

Ah right, you can combine long form to rename bindings, and short form to validate required and define extra keys.

πŸ‘ 2

The same key can appear in long form for binding and in a directive for checking/docs

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

For renaming or nested destructuring

The directives have been extended to support requiring/checking of keys, as well as documenting otherwise not-bound optional keys. This was the optimal place for allowing you to specify without having to restate (once for binding and again for required). The design also supports adopting the new facilities with a minimal change to existing code and breaks nothing. If it's not going to require you to bind every key you require, then there also needs to be a way to talk about required keys. That's new, since destructuring was just about binding before. So you should expect some novelty rather than a contortion of forms for the sake of familiarity. The sooner one embraces the concepts being introduced, enumerating/requirements, deep selection with defaults etc, the less it will seem like some arbitrary syntax mods :)

FWIW @richhickey I have not been surprised by any of it in real world code usage as we're adopting the new features at work.

Also please check out :select, which is now quite powerful

πŸ‘€ 3

I'm loving :select in the code at work πŸ™‚

Deep select is a superpower

Deep select is awesome, and how I thought it would behave when I first heard the feature described. Having select apply post-or is also great. I anticipate one of my use-case for select is to define some inner boundary like internal APIs, so say a config is optional but my downstream functions have it required, I'd assume I would use an :or combined with a :select to default it. Accepting keywords in :or is necessary, but it's also nice that now: {:keys [:a] :or {:a 1}} works πŸ˜›

Also some-vals is going to be useful in interop, trying to make nil values from JSON be missing keys in Clojure was something I did all the time.