Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ClojureScript sorted-map does not print in sorted order for maps with more than 8 elements #645

Open
noahmoss opened this issue Feb 6, 2025 · 7 comments
Labels
bug Something isn't working client-clojure

Comments

@noahmoss
Copy link

noahmoss commented Feb 6, 2025

Hello! I'm using Conjure to work on a project with both Clojure and ClojureScript, and I've noticed a strange discrepancy in evaluating sorted maps:

(def us-states
  {"AK" "Alaska"
   "AL" "Alabama"
   "AR" "Arkansas"
   "AZ" "Arizona"
   "CA" "California"
   "CO" "Colorado"
   "CT" "Connecticut"
   "DE" "Delaware"
   "FL" "Florida"})

(def reverse-states
  (into (sorted-map) us-states)) 

In ClojureScript (with shadow-cljs), evaluating this code, and then evaluating reverse-states on its own, results in this output in the Conjure buffer — clearly not in sorted order:

{"AK" "Alaska",
 "FL" "Florida",
 "CT" "Connecticut",
 "AR" "Arkansas",
 "AL" "Alabama",
 "CA" "California",
 "DE" "Delaware",
 "AZ" "Arizona",
 "CO" "Colorado"}

However, (prn reverse-states) does return a string representation with the keys in sorted order, which is why I suspect this is an issue in Conjure.

Additionally:

  • Removing an element from the original map results in the order being retained when evaluated. It only seems to be an issue for maps with more than 8 elements.
  • Following the same steps in Clojure prints the map in sorted order, for any number of elements.
  • Creating and evaluating the map directly with (sorted-map "AK" "Alaska" ...) doesn't seem to have this issue

Would love some insight into this since it made me mistakenly believe that my code was broken for a while :)

@russtoku
Copy link
Contributor

BTW, the states are ordered in your first code example so I used your second code example.
Sorry, I tried your code and am not able to reproduce your results; i.e., an unsorted map from (into (sorted-map) us-states).

us-states (unsorted map):

{"AK" "Alaska",
 "FL" "Florida",
 "CT" "Connecticut",
 "AR" "Arkansas",
 "AL" "Alabama",
 "CA" "California",
 "DE" "Delaware",
 "AZ" "Arizona",
 "CO" "Colorado"}

reverse-states (sorted map):

{"AK" "Alaska",
 "AL" "Alabama",
 "AR" "Arkansas",
 "AZ" "Arizona",
 "CA" "California",
 "CO" "Colorado",
 "CT" "Connecticut",
 "DE" "Delaware",
 "FL" "Florida"}

What I did:

  • Cloned the Conjure repo to a work directory and changed into it.
  • Ran the dev/clojure/nrepl-shadow.sh script which runs npx shadow-cljs watch app.
  • Edited a new *.cljs file and added your code with unsort map for us-states.
  • Evaluated your forms and got back a sorted map for reverse-states.
  • Verified that Conjure connected to the shadow-cljs watch server on its nrepl port.

What I used:

  • 15-inch M3 Apple Macbook Air.
  • Neovim 0.10.3
  • Conjure (HEAD - commit 95b067e)
  • Node.js v20.18.0
$ npx shadow-cljs watch app
shadow-cljs - config: /Users/russ/Projects/Conjure/russ-try/conjure/shadow-cljs.edn
shadow-cljs - HTTP server available at http://localhost:8080
shadow-cljs - server version: 2.28.20 running at http://localhost:9630
shadow-cljs - nREPL server started on port 54675
shadow-cljs - watching build.
[:app] Configuring build.
[:app] Compiling ...
[:app] Build completed. (119 files, 0 compiled, 0 warnings, 1.03s)

I'm not familiar with using ClojureScript and just followed the Quick Start in the wiki. Could you share what your set-up is?

@Olical Olical added bug Something isn't working client-clojure labels Feb 15, 2025
@Olical
Copy link
Owner

Olical commented Feb 15, 2025

@russtoku after connecting to a shadow-cljs nREPL server you are in a JVM environment, you can then use :ConjureShadowSelect app to drop into the app build. In shadow projects you sometimes have multiple profiles so you need to select the one you're interested in, the JVM process then proxies your evals through to the JS eval environment (your browser or nodejs etc).

When I shadow select and run the code in my browser I do see this issue. My theory is that the nREPL pretty printing function isn't respecting the sorted-map for some reason in shadow-cljs but I'm unsure why. Investigating now.

@Olical
Copy link
Owner

Olical commented Feb 15, 2025

Conjure does no reformatting or sorting of the results, we're just letting CIDER / shadow-cljs do whatever pretty printing it wants to. I'm trying to work out if there's a better pprint function I can pass to the nREPL when in shadow-cljs that will respect the fact that it's a sorted-map.

I suspect the CLJS layer is returning a sorted map which is read as EDN by shadow-cljs in JVM land. It loses the fact that it's a sorted map though because when you EDN roundtrip a sorted map it loses it's sorting since their textual representation is identical.

I suspect any JVM nREPL talking to a shadow-cljs backed app will always lose this information. A raw shadow-cljs REPL will not because you're bypassing that JVM nREPL layer and getting the raw result out of the CLJS side (where it is still sorted).

I feel like this should be a general bug more shadow-cljs + nREPL users have run into, I'm trying to find an open issue about this.

@Olical
Copy link
Owner

Olical commented Feb 15, 2025

I can reproduce this with the rebel readline nREPL CLI client. I think this is a shadow-cljs + JVM nREPL thing I'm afraid. We might have to open an upstream issue with a minimal repro, probably using the rebel readline CLI like I used since it's quite minimal.

Image

$ clojure -Ttools install-latest :lib com.github.bhauman/rebel-readline :coord '{:deps/root "rebel-readline-nrepl"}' :as nrebel
$ clojure -Tnrebel connect :port 36563
(shadow.cljs.devtools.api/nrepl-select :app)

(def us-states
  {"AK" "Alaska"
   "AL" "Alabama"
   "AR" "Arkansas"
   "AZ" "Arizona"
   "CA" "California"
   "CO" "Colorado"
   "CT" "Connecticut"
   "DE" "Delaware"
   "FL" "Florida"})

(into (sorted-map) us-states)

;; => {"AK" "Alaska", "FL" "Florida", "CT" "Connecticut", "AR" "Arkansas", "AL" "Alabama", "CA" "California", "DE" "Delaware", "AZ" "Arizona", "CO" "Colorado"}

Returns the unsorted map, if I prn it then it remains sorted just like in Conjure.

Side note: The whole Clojure CLI / tools.deps stuff is so confusing to me. I will never remember those arguments, it's a shame the Clojure CLI stuff didn't follow in the footsteps of rust, npm/npx, uv etc 😦

Probably not what you wanted to hear, but at least we're getting closer to the root cause!

@noahmoss
Copy link
Author

Thanks for the investigation!

@russtoku
Copy link
Contributor

@Olical shouldn't the definition of us-states be unsorted in ‎dev/clojure/src/dev/sandbox.cljc?

@Olical
Copy link
Owner

Olical commented Feb 16, 2025

That shouldn't matter, the map is parsed and then when you print it back out it can be in any order. I think for tiny maps maybe the order is preserved or something due to a Clojure optimisation under the hood.

So I suppose we could shuffle it in the code, but I don't think it matters in this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working client-clojure
Projects
None yet
Development

No branches or pull requests

3 participants