This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-03-27
Channels
- # announcements (2)
- # architecture (1)
- # aws (3)
- # babashka (50)
- # beginners (99)
- # biff (7)
- # calva (11)
- # clj-http (1)
- # clj-kondo (18)
- # cljs-dev (17)
- # clojars (33)
- # clojure (63)
- # clojure-europe (83)
- # clojure-nl (2)
- # clojure-norway (4)
- # clojure-sweden (18)
- # clojure-uk (2)
- # clojurescript (7)
- # datalevin (3)
- # docker (2)
- # emacs (44)
- # malli (10)
- # music (2)
- # off-topic (2)
- # portal (4)
- # practicalli (2)
- # reagent (23)
- # reitit (6)
- # shadow-cljs (22)
- # spacemacs (8)
- # sql (35)
- # tools-deps (2)
- # xtdb (5)
Posting in here because I think it is relevant for CLJS core team to know:
It looks like the default implementation of -hash
will compute goog/getUid
. This has led to two incidents in the recent past where people are using js/BigInt
and this is causing unexpected results, especially in code that relies on reproducible hashes (e.g. PersistentHashMap). Why? Well according to the Closure library docs, getUid “mutates the object so that further calls with the same object as a parameter returns the same value.” It also mentions it is “unsafe to generate unique ID for function prototypes.”
So I assume it is incorrect for goog/getUid
to be computed on things like BigInts and other objects that do not “behave” like an ordinary JS object, in the sense that you cannot set a field. You can observe this in a REPL as follows:
cljs.user> (def x #js {})
[#js {}]
cljs.user> (goog/getUid x)
101
cljs.user> x
{"closure_uid_121240583" 101}
cljs.user> (def y (js/BigInt 42))
[#object[BigInt 42]]
cljs.user> (goog/getUid y)
104
cljs.user> y
#object[BigInt 42]
Pinging @p-himik since he discovered the hash-code issue a few weeks ago.@hifumi123 you need to supply a -hash
implementation for such types
(For example)
Which isn't a great thing to do on a built-in type, especially if it's done in a library.
is there a good alternative?
in this case users can supply BigInt coefficients to a polynomial, and that data structure stores them in a map
I’m not sure what else to do here, given the errors that can appear once those maps get more than 8 entries
Do you use bigints as map keys or set entries?
> is there a good alternative?
Document what needs to be done if a user wants to use bigints. Optionally, provide an additional namespace that isn't used by anything but that can be required by a user to extend js/BigInt
.
This way, a user has full control over what's going on.
And it's the same for CLJ as well. One shouldn't extend something unconditionally. It might be fine to do in an app, it's absolutely not fine to do in a library unless it's meant to be the center of the universe for any project that uses it.
bigints are also automatically created by the polynomial gcd process… basically the arithmetic forces a promotion outside of the user’s control
@p-himik yes, agree. in this case it kind of is meant to be the center of the universe!
but yeah I do agree that some way to at least opt-out of this would be a good affordance
@p-himik I think supplying a hash code for js/BigInt is fine because there is already examples of native JS types being extended in cljs.core More importantly, BigInts as hash keys work in JVM Clojure, but not CLJS. So implementing IHash would help “converge” the behavior of Clojure and CLJS. Here is a quick demo in a Clojure REPL.
user> {0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 (bigint 9) "I'm accessible!"}
{0 0, 7 7, 1 1, 4 4, 6 6, 3 3, 2 2, 9N "I'm accessible!", 5 5, 8 8}
user> (get *1 (bigint 9))
"I'm accessible!"
I wish the numeric tower were at parity with the JVM version, especially as BigInt is a JS primitive
> in cljs.core
Because that ns is the center of the CLJS universe.
A third-party library is not.
> More importantly, BigInts as hash keys work in JVM Clojure, but not CLJS
That's what CLJS should solve though, or GCL, or a maintainer of an app (so the scope is limited only to that app), not a library.
In fact, all interested parties can already create issues for https://github.com/google/closure-library and vote for https://ask.clojure.org/index.php/10938/can-js-bigint-be-used-as-a-map-key-in-clojurescript and even sign a CA and write a patch. ;)
(Scratched out GCL because goog.getUid
explicitly states that it accepts only objects whereas bigints are primitives).
@dnolen What do you think of explicitly handling js/BigInt
and js/Symbol
(someone else has complained about it before) in the hash
function, given that those create primitives and should not be passed down to goog.getUid
? I couldn't find any previous discussion.
While extending those types to IHash
works, it's problematic because different libraries might do the same thing but use different hashing approaches. E.g. in the case of js/BigInt
djblue/portal
and mentat-collective/emmy
use different radices for turning a bigint into a string and hashing that string.