Clojure 1.13 adds support for checked keys(clojure.org) |
Clojure 1.13 adds support for checked keys(clojure.org) |
I know this sounds unreliable but in practise I like a language that defaults to pragmatic code paths so I don't have to stay up at night imagining a million code paths
This adds a throwing codepath which is quite drastic so I'm glad people don't build this into programs everywhere - I'd be nice to hear what the team imagine as the use case for this
Normally for correctness I'd like to see specs at the boundaries for programs and different test suites for internal behaviours
I have seen multiple major production outages in Golang code because people accidentally read a non-existent map key and used the default value. As a funny bonus in one of those cases we were stumped when debugging because this code had tests, but the tests were also reading the default values out of the map and asserting that "" was in fact a valid textproto (it always is!) so silently testing nothing.
So even if defaults are useful 9/10 times that 1/10 is so painful and expensive that it isn't worth it in my experience. The time spent responding to, debugging and fixing those outages far, far outweighed the time saved by the convenient default values in the 9/10 times.
So nil will have had special consideration in Clojure core functions
That doesn't mean it doesn't crash either it will absolutely be unhappy with nils in your math
nil is usually unexpected in test outputs or at the very least an unhappy path
In terms of debugging that's why I can't quit this language flowstorm let's me visually step through what happened line by line, backwards, forwards, programmatically - whatever
Languages should be competing against each other by their best time travel debugger it just completely removes the need for guesswork
And that is still doable AIUI: they're optional checked keys. The doc describing them makes the distinction between required and non-required keys.
Arguably we already had those: I religiously use spec'ed maps in my Clojure since a great many years (and Clojure spec is still in alpha, but "alpha" in Clojure land basically means: "more stable and less likely to change than any feature in any other language" and I'm only slightly exaggerating here).
In my case I use good old defn-spec (form Orchestra but YMMV) instead of defn. And my maps are (partially) spec'ed, using spec'ed keys (as well as any other non-spec'ed key I feel like using). Sure it's only runtime checks but it's really great.
You get to both have the extensibility (you can for example add keys that don't exist yet later on without changing any of your specs) and you can specify which keys are required.
For there is such a thing as maps where you know that this and that key must always be there.
I don't think it's an issue to have optional checked keys. Especially not when you can mix both required and non-required keys in the same map.
I haven't used it, so I don't know its tradeoffs; but its docs say its types exist at compile time: https://github.com/clojure/core.typed/wiki/User-Guide
It just doesn't make much sense to do - most modern developers will be running static analysers through LSP or their editor (knowingly or not) continuously on code change so as to see those errors quicker than re-compiling the program
(defn foo [{:keys [a b c] :or {a 1}]
{:pre [(some? b) (some? c]}
.....
having `:keys!` will automatically remove the need for `:pre`You can add a third parameter to override the nil if detecting the missing key matters.
(You almost surely know this, but not all HN commenters will.)
user> (:bar {:foo 1})
nil
user> (:bar {:foo 1} :missing)
:missing (defn my-function
[{:keys! [username]
:keys [firstname
lastname]}]
(do-stuff username
firstname
lastname))
A minor downside is that now it seems `nil` is even more overloaded b/c you can explicitly pass in a nil and give it a special meaning. This generally cascades in to messyness (better to have a special key like `:missing-username`).Feels like throwing an error on nil would have been better/simpler? But I'm sure there's an angle I've not considered
> Clojure’s idiomatic use of maps has proven valuable, but missing required keys, misspelled keys, and invalid values can lead to failures that do not connect to the actual source of the problem (e.g. NPEs) making diagnosis difficult. At the same time, Clojure lacks a simple inline mechanism for functions to document and check the keys they require and accept. Existing tools either separate those expectations from the function itself or couple data shape and data provision.
Any news on ClojureScript gaining the feature?
Thanks for everything you've done for Clojure and ClojureScript, I'd surely have dropped programming as a whole if I didn't discover Clojure and ClojureScript at the time I did.
It’ll be nice to have it at hand in the base language though.
This will eliminate two whole classes of errors: 1) where keys are supplied a value at an undesired nesting-level. 2) where keys are not-yet-set for some other reason.
For the many programmers who have to write in checks and verifications themselves for this, this saves quite a bit of time, removing the interruption from coding and restoring the flow of getting logic-to-symbol.
Using structural editing idioms and the REPL, usually makes the process less vexing.
Might as well have been Russian.
Now it's as natural as any other language.
`(if-not key1 (throw (Exception ...))`
...and pre-conditions, e.g. `:pre [condition1 condition2]` do not run when `assert` is off.
def foo(*, a, b): return a+b
which errors out at runtime if `a` or `b` are omitted, despite being keyword arguments which are usually optional.Many people (including myself) already have checked key variants for maps; this mainly extends the syntax to destructuring too.
I feel there are better solutions to this problem that already exist, such as using spec/malli and validating the value properly rather than just checking for presence.
So less about ClojureScript specifically, and more generally how I think we're well situated for people looking for a way out. The current mainstream practice dead end is bigger than the one that made React (also originally just a script tag include) appealing to me back in 2013. There are of course many ways forward that don't involve CLJS, but I think ClojureScript/GCL and the new crop of NPM-dep free pure CLJS solutions like Replicant are well situated for folks who can see that accepted practices are not delivering enough value even with AI assistance.
Namaste!
(cons some-value my-list)
If my-list is nil, then the above expression will result in a list containing only the element some-value. Otherwise it will be a list starting with some-value followed by any other values that have been previously in my-list.