This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-01-02
Channels
- # announcements (7)
- # atom-editor (3)
- # babashka (8)
- # beginners (38)
- # biff (5)
- # calva (17)
- # cider (26)
- # clj-kondo (6)
- # clojure (49)
- # clojure-europe (47)
- # clojure-norway (19)
- # clojure-sweden (2)
- # clojure-uk (1)
- # clojurescript (22)
- # cursive (20)
- # datahike (1)
- # datomic (6)
- # etaoin (2)
- # honeysql (2)
- # hyperfiddle (36)
- # jobs-discuss (19)
- # leiningen (15)
- # malli (5)
- # off-topic (8)
- # overtone (1)
- # pathom (15)
- # pedestal (8)
- # polylith (4)
- # releases (1)
- # ring (5)
- # schema (10)
- # shadow-cljs (17)
- # timbre (3)
- # xtdb (17)
Is it possible to create a map where access via keys other than specified few result in error?
it is possible, it is all interfaces and you just implement the right ones and you can make all kinds of weird map types. if that is safer or not is debatable
it is almost always this case that you want to specify that a certain subset of information is present, but allow for more information than that to be present (this is the basis for things like sub typing in static type systems, etc)
sure, but often for modeling domains this additional capability is less value than ability to catch incorrect use of the interface
(get my-map :unknown-key ::not-found)
=> ::not-found
so you can check for it that way, or (contains? my-map :some-key)
=> false
if the key isn't in the map.
Clojure Spec can be good for validation around the edge of your domain -- but it's design, along with Clojure's design, is that maps should be open for extension so additional keys should be accepted (and ignored, if they aren't "useful").
For specific situations where you require no additional keys, you can either select-keys
to get the known subset you want or check (set (keys my-map))
against a valid set of keys.
If you are open-mindedly desperate for bad ideas, then another thing you can do is adopt the regimen of referring to map entries not by keyword but by var. That is, (def somekey :somekey)
and thereafter use somekey
as the map key. If you misspell it, the compiler will tell you. But, on balance, wouldn't it be better to focus on the positive - if the program is using a wrong keyword, and yet all the tests pass, then evidently the program is working fine and it's not a problem.
(of course throw something useful with ex-info, I am just really lazy about using assert nil when I want to turn a branch into an exception)
thank you for the ideas everyone. so in essence, no library exists that does this? or any resources as to how to implement such a thing
Maybe something like this could be helpful? https://blog.wsscode.com/guide-to-custom-map-types/
> no library exists that does this It's more that what you're wanting to do is probably a bad idea and goes against the design principles of Clojure 🙂
Wanting some degree of safety is perfectly fine though, there isn't a single programming model / set of guidelines that will satisfy each and every Clojure team out there.
An unsuspected solution is to roll your own get
/ get-in
and ban their clojure.core counterparts https://github.com/clj-kondo/clj-kondo/blob/be374eac1814c0d98c4fa6616c24a84478ce588c/doc/linters.md#discouraged-var.
This technique sounds safer to me than the bespoke map, since it works with every map:
• You don't have to remember using the custom map each time
• You can freely use assoc, dissoc, or similar functions which might discard the custom map and return a vanilla one
☝️:skin-tone-2: Plus also ban (my-map some-key)
and (:a-key my-map)
and using keywords as functions, e.g., (map :a-key coll-of-maps)
-- not sure how enforceable that would be automatically...?
Map-as-function sounds the hardest 🙂
Personally get-in
is the most interesting function to add safety to anyway, since nested paths quite often break with refactorings or similar changes
(I will note that in 13 years of production Clojure work and now with 140k lines of Clojure, I have never felt the need for such a data structure 🙂 -- select-keys
, contains?
, and Spec have always been sufficient to ensure "bad" keys don't end up where they should not be... wow, Spec has been around for almost eight years now! May 24th, 2016 was the first alpha version!)
thanks @U45T93RA6 , that sounds like a much simpler workaround to test this out
How do you submit a bug for clojure.data.json
(or at least I think it's a bug/should fail on assertion)?
(require '[clojure.data.json :as json])
(json/write-str {:a 1 "a" 2})
;; => "{\"a\":1,\"a\":2}"
Usually, the best place is to submit a question on https://ask.clojure.org/. You can also check if it's already been addressed before.
I couldn't find a similar question, https://ask.clojure.org/index.php/questions/contrib-libs/data-json.
this is by design
so, not a bug - see the doc for write under key-fn http://clojure.github.io/data.json/#clojure.data.json/write
json keys are always strings, keywords and symbols use the symbol/keyword name by default, everything else stringifies (strings are of course always strings). you can overload :key-fn if you want other behavior
The docstring there only addresses the default behavior of keyword->name, unless I'm missing something
"Default calls clojure.core/name on symbols and keywords and clojure.core/str on everything else." covers both ?
How does that address that the output containing duplicate strings? It's on the user to know the default behavior outputting a string will produce an invalid json in this case?
oh, I didn't even notice that aspect of your example :) but yes, that's going to be on you. it's unusual to have a Clojure map with both keyword and strings (which may overlap), so I think you'd want to apply some extra cleanup before json-ifying
the cost of doing that for all writes would be high in data.json
Should I register this as a question for others to see/know about? I know Slack also gets indexed for search
strictly speaking, duplicate keys are not wrong according to the json specification.
is there any built in way or library to relate pprint output location to edn paths? eg. if (= (with-out-str (pprint {:a [1 2 3]})) "{:a \n[1 2 3]}") then (= (imaginary-fn {:a [1 2 3]} [:a 2]) [10 11]) so converting from the edn-path for get-in of the first fn (or some other data-oriented path) to the index of the characters representing it in the output?
You can do things like this with https://github.com/clj-commons/rewrite-clj.
For examples, you can check out neil which will can update a deps.edn
file while preserving formatting, https://github.com/babashka/neil/blob/main/src/babashka/neil.clj
thanks, I was just looking at zprint which is built on top of it, thinking about it pprint is kind of part of a linter
I guess neil is built on https://github.com/borkdude/rewrite-edn which uses rewrite-clj under the hood