This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-06-24
Channels
- # announcements (1)
- # beginners (78)
- # cider (19)
- # circleci (4)
- # clojure (27)
- # clojure-denmark (3)
- # clojure-europe (29)
- # clojure-gamedev (1)
- # clojure-madison (1)
- # clojure-nl (2)
- # clojure-norway (37)
- # clojure-sweden (5)
- # clojure-uk (4)
- # core-typed (45)
- # datahike (5)
- # fulcro (1)
- # graalvm (17)
- # gratitude (1)
- # hyperfiddle (3)
- # lsp (17)
- # malli (1)
- # off-topic (3)
- # pathom (11)
- # polylith (7)
- # re-frame (13)
- # releases (1)
- # ring (20)
- # scittle (21)
- # specter (4)
- # tools-build (8)
- # xtdb (10)
- # yamlscript (7)
https://clojureverse.org/t/should-we-really-use-clojures-syntax-for-namespaced-keys/1516/32 Hello, I came across above thread which I found really interesting especially because i am facing the same dilemma with passing data from/to js. I want to know more about the experiences of people who did clojure+js projects, how do you handle namespace(d) keys?
One thing that has changed since that article is the rise of Malli, so a lot of people, by inertia are defaulting to non-namespaced keys. Most often Malli schemas are associated to a Var, so attribute namespacing is redundant. While Spec nudged one more heavily to use namespaced keys. (For clarity, neither tool really enforces a choice - just speaking about what feels 'natural' to most people) Nowadays, ideally I'll use non-namespaced keys for 'model' data (anything that might flow DB<->API), and namespaced keys for code (everything else) Although I'd happily change my mind if tooling support was different.
FWIW, I think that even with tooling support the model data wouldn't benefit from namespaced keys, at least in all the code that I've written. I remember trying this approach and dropping it because if previously I could create e.g. a label view that could be used for all items that have a label, now I have to also pass the label key all over the place, right from the moment I learn the type of a piece of data. The only instance where namespaced keys would help is when mixing data from different models. But I never do this, and don't remember seeing anyone do this except for maybe some SQL queries with a join. But at the join level, I also haven't seen any use cases where automatic namespaced keywords would be strictly better than clear explicit aliases.
I can see that being practical. I guess what db is selected is a major factor (in my case, datomic). Models are expected to be namespaced in this particular case.
an idea mentioned in the thread is to transform data on the edges by dropping or adding namespaces. Major gripe i have with this is data loss, this is especially painful for nested maps. thus, following the same principle, instead of dropping namespace, they'd be grouped. for example:
;; model
{:user/firstname "Hasan"
:user/lastname "Ahmed"}
;; at response becomes
{:user {:firstname "Hasan" :lastname "Ahmed}}
I hate the magic involved in this, but it's pretty trivial to implement (with some middlewares), then js/ts project is unaffected by clojure quirks and clojure things are handled as usual
how bad of an idea is this?We used namespaced keys at work quite a lot -- for our domain model and also as an artifact of using next.jdbc
with the default options.
If you're generating JSON, the libraries all have an option to create unqualified "key" names for JSON so that's just not an issue.
Incoming JSON is going to go through validation anyway so you'll validate it (as string-keys, ideally) and then build your (qualified key-based) domain model for internal use, and then as it passes out the other end of your code, either qualified keys matter (e.g., Datomic etc) or the persistence/JSON output will accept qualified keys and produce unqualified data (e.g., next.jdbc.sql
accepts qualified keys and strips the qualifier).
I will also say that, for the most part, the JSON APIs provided by our code are natural for JS and don't match what is natural for our domain model, nor entirely match our persistence model -- so there's already a mapping at the edges. That's why I'm a big advocate of qualified keys in the domain model -- because you are likely to have a transformation from/to JS data anyway (or from/to SQL or whatever). And if it so happens that your DB can handle your domain model as-is -- that's a win because you have less transformation to do.
(FWIW, I strongly disagreed with most of Val's post and I think that "discussion" happened either on Reddit or directly on his blog and I didn't want to repeat it all on ClojureVerse -- and it's also a six year old post that I think hasn't aged well with the way things have gone since then in the Clojure world)
This is the approach I currently following, receive unqualified then qualify then unqualify. It is an overhead that I'd like to get rid off. For every model I create, I have to have two interfaces/schemas and bi directional transformations
There is the option of accepting and returning everything fully qualified and in js variables are accessed using 'user["user/firstname"]' instead of 'user.firstname'
But this might cause things to break (i have not tested but something like v-bind on vue might have weird behaviours)
It shouldn't just be qualify/unqualify tho', field names, overall data shapes, most of that should be transformed from what makes a good API to what makes a good domain model to what makes a good persistence model. Those three are (should be) all different so the transformation is already necessary -- and qualify/unqualify is the least of your concerns.
(although with Datomic, you can probably omit the transformation between the domain model and the persistence model)
You have any public project, you can point me to? I'd like to learn more about this
Sorry, our apps are all private code (146k lines).
That's alright. Thanks for everything
I prefer Approach 2 (systematic translation) and don't find the arguments against it persuasive. There should be a translation layer at the program boundary already/anyway. There's nothing "hacky" about it.
I have used the munge/demunge approach with relative success.
I was frustrated that up to four people in that discussion mentioned munge
and didnt acknowledge the existence of demunge
. In CLJS, demunge
exists in cljs.core, but in CLJ it exists in clojure.lang.Compiler, also exposed publicly in clojure.main and clojure.repl.
As far as I can tell, its totally safe to use demunge
and its likely not in clojure.core because the core team sees no reason to add public functions to clojure.core when said public functions already exist elsewhere
Has anyone had the same issue? I tried to disable signature-help for clojure-mode but couldn't get around it.
(global-eldoc-mode -1)
(after! lsp-ui
(setq! lsp-ui-peek-enable t)
(setq! lsp-ui-doc-show-with-mouse nil)
(setq! lsp-ui-doc-show-with-cursor nil))
(after! lsp-mode
(map! :map lsp-mode-map
"M-?" #'lsp-ui-peek-find-references)
(setq! lsp-ui-doc-enable nil)
(setq! lsp-eldoc-render-all nil)
(setq! lsp-eldoc-enable-hover nil)
(setq! lsp-signature-auto-activate nil)
(setq! lsp-signature-render-documentation nil))
(setq! cider-use-tooltips nil)
(setq! cider-eldoc-display-for-symbol-at-point nil)
(setq! cider-eldoc-display-context-dependent-info nil)