It seems like there should be a way to solve this problem using a core function/macro in CLJS without needing goog.object. Perhaps unchecked-get (or something similar) should become part of the ClojureScript core API?
@borkdude Does squint have built-in for this?
cljs.user> (aget #js{"foo" "bar"} "foo")
"bar"In CLJS I use unchecked-get or aget . In squint you can just use get
@dnolen Any reason why unchecked-get shouldn't become "official" at this point in time?
Since https://clojurescript.org/news/2017-07-14-checked-array-access talks about avoiding aget.
I know, but it works well and will continue to work well
I read the objection in the blog post. unchecked-get it is then
which is not public API :)
Yes. Exactly.
> In the relatively few places where it is not (in highly performance-critical areas in the standard library implmentation), the compiler makes use of a new internal unchecked-get macro to get the job done.
>
> We’d encourage you to revise code you control to to ensure that the aget and aset are used only for array access, and to consider using the facilities in goog.object (either directly, or indirectly via a library such as https://github.com/binaryage/cljs-oops) to access object properties.
yeah I've used goog.object too a lot
but I don't always want to pull that in
Exactly!
We've had unchecked-get for 9 years. Time to make it official? Or define an official thing?
(obj-get ...) (obj-set! ... ...) would be nice additions
I'm sure there's JIRA issues for it
This also works in JS:
(doto #js {:a 1} (js/Reflect.set "a" 2))
=> #js {:a 2}
(js/Reflect.get #js {:a 1} "a") => 1Looks like the work for unchecked-get was done on https://clojure.atlassian.net/browse/CLJS-2198
I've never seen the CLJS compiler complain about checked array access in any of my projects, maybe that flag isn't used a lot?
Personally, I always used aget, and after that change, and went back and changed some of my browser-deployed code to use clj-oops. I'd love a built-in.
I guess this would work well too
(defn oget [obj x] (js* "(~{}[~{}])" obj x))aget is for arrays like Clojure - unchecked-get is just a lower level thing which, no reason to use it except for performance.
GCL is more or less the standard library, even more so now that we forked it.
I just use goog.object - or newer Object methods in all cases.
unchecked-get is just way too long of a name for a normal pattern, and it's name more or tells you it's a special case. gobj/get is shorter
is there a reason get can't be extended to work like this?
it's impossible to detect object cases
whatever we're going to do it's not going to involve get
the other thing is that generally you will want more complicated object access patterns
GCL has a bunch of stuff w/ a eye for performance w/o bringing a whole bunch of unrelated code to accomplish that.
I've had the use case come up enough times in my own code that I'd love to have an object get that didn't require me to pull in GCL.
For me, at least, it feels like a missing piece of JS interop. It's inaccessible in core CLJS.
"pulling in GCL" probably isn't as heavy as it feels, goog.object/get compiles to the exact things mentioned in the original post: https://github.com/google/closure-library/blob/b312823ec5f84239ff1db7526f4a75cba0420a33/closure/goog/object/object.js#L349-L354
I think @nbtheduke's question is the typical question I see: What's the interop form for foo[bar]?
It feels especially strange to me because foo["bar"] has language-level support via (.-bar foo).
I guess people just want something like unchecked-get but public and blessed. I'd like it too.
yes, that is my point
Perhaps from the maintainer's point of view I get it: there's other patterns like getting stuff in nested objects that you would often need too, so it might be a slippery slope and goog.object already has a bunch of that stuff
compare with fennel (clojure-like compiling to lua), where (.bar foo) becomes foo.bar and (. foo bar) becomes foo[bar]
Perhaps some of goog.object can be auto-referred (and maybe renamed) and if not used, it will be optimized away?
for the time being, i'll rely on unchecked-get as it directly compiles to what i want
to veer wildly into early solutions lol, may i suggest (.- foo bar) as it currently doesn't compile and imo is the natural extension of the dot dash syntax
Here's the discussion where @pez ran into issues. It was a problem with "-", but also the different, slightly incompatible ways. https://clojurians.slack.com/archives/C03S1L9DN/p1755155632719919
oof that's rough
"pulling in GCL" is the wrong way to think about, in reality using anything from cljs.core involves a lot more pulling, GCL is tree-shaking friendly, cljs.core not so much
property access is for web stuff, GCL data types, and CLJS data types. Stuff that Closure will see. "data" objects i.e. JSON things need goog.object
people try all kinds of things, but IMO if you just stick w/ goog.object/get for JSON and property access only for deftype and known Web APIs you can avoid a lot of trouble.
I'm not saying we shouldn't work on this - but that's approach I've used for the past 9 years or so and it's relatively headache free, I type a few more characters but I don't really think about this problem at all.
any proposal needs to be simpler
how receptive would you be to expanding (or accepting a PR to expand) the clojurescript site to describe the core team's stance/suggestions?
i read every page and it didn't cover this which is why i came here
I mean the first this is foo[x] in JS is completely ambiguous
if foo is an array and x is "10000" what happens
you get undefined
and what if you assign
you get sparse array, all kinds of weird stuff happens
well, we're not discussing assignment
my point is that the JS overloaded []
historically people (ab)used aget aset together
sure, js has made some poor decisions lol
In any case that's how we ended up here - people abused this pattern in CLJS - new names are awkward. Thus the above recommendations
Making what we recommend clearer is a good idea of course.
right now there's no documented method outside of the news post linked above to perform an object get with a variable key (`foo[bar]`, the way to do it in vanilla javascript), so outside of foundational language changes, i'm looking to ease future pain for other folks with documentation
The way I'm thinking about it is from an interop point of view. JS has all sorts of ugly and nasty behavior, but if I need to construct it, then I need an interop form/macro. Right now, there is no clear way to construct obj[key] from CLJS. People in the community keep recommending unchecked-get.
pointing devs to the GCL documentation only does so much because you don't know what you don't know lol
I actually want the exact semantics of the host language. I don't want something that tries to protect me from it, because I'm trying to do interop.
i'm willing to write the documentation into the Host Interop section, it's currently sparse compared to the extended docs for Clojure and could use some love
again if people want to rely on the details then aset suffices no? but again just uninteresting to make this recommendation to me.
again "I want the same exact semantics of the host language" is not what everyone wants
i thought aset was changed to only work with arrays, which is why i didn't reach for it
some people are writing mostly CLJS and they keep the stuff at the edges
aset producing a warning not an error and you can suppress it like any other warning
It's discourages the pattern from libraries, but I don't really a see a problem with that, the bar for libraries is higher in the interop area because of tree-shaking, perf etc.
for apps, doesn't matter that much other than philosophy
if you don't believe this is a worthwhile feature of javascript to support (in syntax or blessed function), then i will drop it and focus on improving the documentation
I'd be ok w/ documentation talking about goog.object/get but also pointing out that in JS there are only really objects in the early spec, unlike Clojure - so you could use aget and you could suppress the warning if that's desired for whatever reason.
what do you mean by "early spec"?
early JS spec. the other thing that is worth pointing out is that none of this addresses the biggest problem
i.e. if you use prop access on JSON, or you use aget alternative on deftype - both will lead to a broken build.
so if we do add something perhaps a documentation refresh is in order here - and I'm happy to assist and write something etc.
you mean JSON.parse output? or something else
any JS value the Closure Compiler will never see the definition of has this problem, but it rears its head most often with APIs that take JSON/like values
I do agree with improving the docs and putting the nuances in the official reference. It's good to know that the aget idiom will continue to work too. My impression was that it could break sometime in the future.
Thank you @dnolen for letting us drag you into the conversation!