Hey all, I need a hand getting oriented. I have zero experience with cljs, and contributed a PR to a cljc project. Everything is ok in the clj side, but in cljs tests fail in a rather opaque manner:
actual: #object[RangeError RangeError: Maximum call stack size exceeded]
There's no stack trace or hint to the root cause, and to my embarrassment, I have no clue how to even start a cljs repl or recreate it in a manner I can debug.
Can you please advise?
(link to PR in thread)two things. you can't rely on identical? when it comes to keywords in CLJS, hence there is a keyword-identical? you are supposed to used, or just = (which checks identical? as its first step anyway)
second you are using (type x), which unlike CLJ is not really a "class"
a REPL you could get via clj -Sdeps '{:paths["src"],:deps{thheller/shadow-cljs{:mvn/version"3.1.7"}}}' -M -m shadow.cljs.devtools.cli browser-repl in that repo
(also opens nrepl server, which you can connect your editor to via .shadow-cljs/nrepl.port)
I just figured out how to launch the embedded node REPL from within a lein repl
At least I get a stack trace now
I suspect it's not a full stack trace though
What should I use instead of type?
I don't know it thats actually related to the problem. JS just doesn't have a real class system and type just gives you the constructor fn
I'm assuming the error I'm getting is justified in that I just exceeded the maximum call stack size
the test data doesn't look like anything that should reach even close to that limit
maybe it just gets stuck in an infite recur loop?
No, I increased the stack size and the test passed
The lack of keyword-identical? in Clojure is a hzaard (or in other words, the lack of clj/cljs parity is a hazard and I guess it could be resolved more easily by the addition of that abomination to Clojure than by its re-removal from ClojureScript, which originally did not have it). I did not see an "ask" already, so I submitted one: https://ask.clojure.org/index.php/14593/could-keyword-identical-be-added-to-clojure
Feel free to vote on it if you agree.
its tricky. better just not use a keyword here to begin with. I'd rather remove the reason why keyword-identical? exists in the first place. but thats another very deep rabbit hole 😛
Or have identical? handle the special case of keywords in cljs. Then we would have a unified interface and correct functionality in both.
It my call a private keyword-identical?function. Or am I missing something fundamental here?
changing identical? makes it slower for the "normal" case, so not a worthy trade off
problem is that keywords in CLJS are not interned like they are in CLJ, which has to do with :advanced dead-code removal
so in development each time a keyword is used its a literal new cljs.core.Keyword in the JS code, hence never identical?
in optimized builds thats actually optimized out, so it is identical? there (for this case, not always)
so its better to just use a definite unique value like (js/Object.) or (Object.) in CLJ, and not relying on the fact that keywords are interned or not
(defn- cached-override?
[cache x]
(let [clazz (type x)
not-found (#?(:clj (Object.) :cljs (js-obj)))
ret (get @cache clazz not-found)]
(if (identical? not-found ret)
(let [ret (satisfies? IOverride x)]
(vswap! cache assoc clazz ret)
ret)
ret)))something like that. could move that obj into a top-level def to avoid the allocation, but you get the idea
Will that top level def "behave", for lack of a better word, in cljs?
They'll have to be top level for correctness 🤔
(def not-found (#?(:clj (Object.) :cljs (js-obj))))? of course
I think there's an extra paren pair there, but sure
right yeah
(def not-found #?(:clj (Object.) :cljs (js-obj)))
(defn- cached-override?
[cache x]
(let [clazz (type x)
ret (get @cache clazz not-found)]
(if (identical? not-found ret)
(let [ret (satisfies? IOverride x)]
(vswap! cache assoc clazz ret)
ret)
ret)))