Fork me on GitHub
#clojurescript
<
2021-03-25
>
Timofey Sitnikov10:03:17

Good morning Clojurians, I am trying to use cljsjs.material-ui using Clojure CLI (deps.edn) where I specify the material UI as one of the deps like so: cljsjs/material-ui {:mvn/version "4.11.0-0"}. When I require it, like so: [cljsjs.material-ui] and try watch the app using shadow-cljs, I get this error: The required namespace "cljsjs.material-ui" is not available, it was required by "app/ui/root.cljs". Is it possible to include this package using Clojure CLI?

nilern10:03:32

You could also use npm or yarn instead of cljsjs if you are using shadow, but I don't know if that would solve this.

☝️ 3
Timofey Sitnikov11:03:03

@U4MB6UKDL, OK that works very well, thank you ...

thheller11:03:07

@timofey.sitnikov shadow-cljs does not support CLJSJS packages, you should use the npm package directly instead.

3
pinkfrog15:03:35

Using cursive and seeing many unresolved symbols like rn/View, where rn is from (require “react-native” :as rn)

p-himik15:03:50

That would be a question for #cursive, but what you see is expected - Cursive doesn't handle NPM dependencies yet.

p-himik15:03:18

To be clear - the code will work just fine. It's just the IDE that doesn't know how to resolve the symbols.

pinkfrog15:03:16

Do you know how to turn that thing off.

pinkfrog15:03:18

Kinda annoying

p-himik15:03:05

You can turn all symbol resolution warnings.

borkdude16:03:48

FWIW clj-kondo has the convention of not warning for npm libraries which are required with a string name

borkdude16:03:52

maybe cursive has the same?

p-himik16:03:27

Nope. But I guess you could disable the Cursive warning and leave only the warning from clj-kondo?

cfleming20:03:25

Cursive doesn’t have the same, but should have.

🙏 3
Adam Helins17:03:36

After hours of fighting, I believe I have actually uncovered a bug in CLJS. This misbehaves weirdly while in Clojure, it does not:

(def tree
     (sorted-map 3
                 (sorted-map 1
                             {:a 'leaf-A})))

(def subtree
     (sorted-map 1
                 (sorted-map 100
                             {:b 'leaf-B})))

;; Fine, of course, but now...

(assoc tree
       3
       subtree)

;; Throws:   Error: Cannot compare :a to 100
In Clojure JVM, the subtree gets assoc'ed as expected and that's it. In CLJS, given the error, it looks like it is doing some kind of merge... But why?! That part of the original tree should not be considered at all, should it?

🙌 3
andy.fingerhut20:03:59

Unless you look under the hood at a particular tree shape for a sorted-map, you have no way to know which keys will be compared to each other via the comparator function, and which will not. It is only going to give predictable behavior if all of the keys in a sorted map can be compared to each other without throwing exceptions.

andy.fingerhut20:03:36

Basically, in this particular situation, you just got lucky that Clojure/JVM did not compare the two keys and throw an exception.

andy.fingerhut20:03:38

Hmm, unless I am misunderstanding the example, which I think I might be. Thinking a bit longer on this ...

andy.fingerhut20:03:08

I think the difference between Clojure and ClojureScript in this case is because Clojure when doing assoc, checks for an already-existing key whether the current val is identical? to the new val, which is always a fast check that can never throw an exception, and if (identical? old-val new-val) returns true, the assoc implementation optimizes by returning the current collection, not a new one.

andy.fingerhut20:03:33

ClojureScript instead does = on the new and old value, which is a deep comparison if they are not identical objects in memory, which leads to the exception you are seeing.

andy.fingerhut20:03:20

I don't know whether cljs maintainers would consider this a bug or not, but at least that is the root cause of the different behavior here.

andy.fingerhut20:03:19

Here is the relevant line of the Clojure implementation doing Java identity comparison: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentTreeMap.java#L132

andy.fingerhut20:03:24

with a comment about it, even

andy.fingerhut20:03:32

Here is the corresponding line in cljs implementation that uses cljs = instead: https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L8857

andy.fingerhut20:03:11

I have to say, that is a pretty subtle difference

raspasov20:03:14

I am not an internals expert, but that feels like it should be a bug.

👍 3
raspasov21:03:58

@U0CMVHBL2 as far as I understand, this is specific to sorted-map, is that right?

andy.fingerhut21:03:23

It looks like cljs PersistentHashMap uses identical? rather than = in this case, as far as a quick check of the code by me can tell.

andy.fingerhut21:03:40

So perhaps only PersistentTreeMap in cljs has this behavior.

andy.fingerhut21:03:05

which is the type used to implemented sorted-map, so short answer "yes" 🙂

raspasov21:03:57

Yes, that’s what I thought 🙂 Hmm

raspasov21:03:14

It is a pretty trippy difference, and it only shows up in certain cases:

(comment

  (def tree
    (sorted-map 3
                (sorted-map 1
                            {:a 'leaf-A})))
  (def subtree
    (sorted-map 1
                (sorted-map 100
                            {:b 'leaf-B})))


  (assoc tree 3 (into {} [[1 (sorted-map 42 nil)]]))
  ;=> {3 {1 {42 nil}}}

  (assoc tree 3 (into (sorted-map) [[1 (sorted-map 42 nil)]]))
  ;=> Error: Cannot compare :a to 42

  )

👍 3
andy.fingerhut21:03:45

Sometimes you get lucky and a sorted-map comparison doesn't compare bad pairs of keys with each other, depending upon the tree structure, as I mentioned above.

andy.fingerhut21:03:12

but there are 0 promises you will get lucky like that. You should assume you get unlucky.

raspasov21:03:14

I very rarely (and I assume most people also) use sorted-map, so that’s why this probably hasn’t surfaced earlier

andy.fingerhut21:03:36

They do have fairly limited use, and they are less time-efficient than regular maps.

raspasov21:03:07

Yes, but when you need a sorted map, you really need it 🙂

andy.fingerhut21:03:09

And this issue probably only arises with a sorted-map nested inside another sorted-map, in cljs

raspasov21:03:09

I’ve made a good use of them for sorted lists in UIs at some points, for example.

andy.fingerhut21:03:57

If someone wants to file a question at http://ask.clojure.org asking whether this is considered a bug or not, feel free to include any or all of the details above pointing at the code implementation difference, which would help whoever might change the cljs behavior

👍 6
Adam Helins08:03:58

@U0CMVHBL2 Thanks for the detailed answers, you are probably onto something with identical? vs = Still feels like a bug however, doesn't it? That :a keyword in our examples should not have anything to do with the new tree. Logically speaking, Clojure's implementation is intuitive and CLJS's one is not, in my opinion. I'll post this on http://ask.clojure.org 😉

Adam Helins08:03:49

@U050KSS8M And yes, sorted map are definitely rare in code bases but for some problems they are a must and become extremely useful

raspasov08:03:24

@UCFG3SDFV I completely agree

raspasov08:03:58

I was just speculating why this has stayed hidden for so long

Adam Helins08:03:20

I discovered this while working on: https://github.com/helins/rktree.cljc It must be one of the very few existing examples where nested sorted maps are used systematically. Even then, it took me a while and escaped tests. This error happens only in that specific scenario. Other forms of nested sorted maps and various updates are just fine.

Ronny Li17:03:35

Hi everyone, how reliable is https://github.com/lbradstreet/cljs-uuid-utils/blob/master/src/cljs_uuid_utils/core.cljs#L60-L78 for creating globally unique identifiers? Can I use it to create unique IDs in my database?

raspasov00:03:54

FYI, ClojureScript also has (cljs.core/random-uuid)

🙏 3
raspasov00:03:11

But Clojure does not have this function in clojure.core

raspasov00:03:11

For Clojure:

(java.util.UUID/randomUUID)

nilern10:03:56

FYI databases can also do this e.g. CREATE TABLE tabe (id text NOT NULL DEFAULT uuid_generate_v4(), ...

🙏 3