clojurescript 2025-12-05

I am trying to roundtrip some EDN in cljs with records and it doesn’t work:

(cljs.tools.reader.edn/read-string (pr-str (->SerRatio 1 2)))
No reader function for tag roklenarcic.math.SerRatio.
=> :repl/exception!
The pr-str part outputs what you’d expect

You have to supply a map of readers as the second argument, otherwise read-string can’t possibly know about custom data types

But it’s a record… shouldn’t it know about that?

But it’s “your” record :)

SerRatio in this case. You can have many other record types

Stuff like this should work in Clojure, IIRC. But not in ClojureScript because there's no global registry of FQN->constructor for records. Due to DCE and minification most of the names are shortened, so your foo.bar.SerRatio ends up as just some x somewhere - no way to reconstruct it dynamically.

interesting, I think this can work

const recordsReg = { 'foo.bar.SerRatio': x } // x -> foo.bar.SerRatio
with some compile time introspection the registry can be populated with type name, and actual value is just a reference

but yeah this will break DCE

everything registry based is going to retain stuff after advanced compilation (spec etc)

Ok, well I went with Transit instead of EDN now

I remembered that existed

transit is better though

I mean it’s similar and it’s another library in the stack, I don’t like adding too much of those

but its more performant and compact than EDN, so I pretty much always use it

It’s more compact? The JSON thing looks like it has quite a bit of extra characters, but that’s just my feeling, I didn’t measure it

well depends on size of the payload, but the larger the structure the bigger the "savings"

could I be using msgpack for shuttling from clj to cljs

you can, but I never have. it'll be slower as well probably.

I wonder if it’s better

I wonder how that looks if the payload is compressed?

doesn't look good, also parsing perf in a browser is not that great

might be still a good option for streaming

IIRC those benchmarks from Peter were done also in a browser. But it could be some custom code for Sente, I haven't checked.

And of course, benchmark results could also greatly depend on both payload size and the actual data. It's one thing to serialize+compress (repeat 1000000 {:some-key 1}) and another (repeatedly 1000000 (fn [] {(random-keyword) (rand-int 1000)})).

yeah but nobody ever uses (random-keyword) keys 😉

I took json payload from this generator https://json-generator.com/ looks like a typical api response you'd have on the client side

Well, yes and no. Depends on what you send. It could be that you only pass some specific datums around - not many keywords, just user-generated data. Or UUIDs with barely anything else. Maybe you decided to save some bandwidth and not pass any keywords around and rely on zipmap on client/server with a known set of keys. Hard to come up with accurate comparisons without representative data from a specific use case. The Peter's benchmark was on some data that I had - looong vectors of maps with the same keywords across all maps. Not everyone will have such payload. But mine payload was arguably optimal for Transit and compression, and yet msgpack ended up winning with Peter's code.

Are wrapped objects to be expected when using proxy with non-primitives?

proxy is recursive, the maps will also be proxy-wrapped, hence the object printing

Ah, that explains it then. This is not a suitable use case then unless you’re happy unwrapping at a later stage. It seems that this includes nil, which doesn’t appear to map to null.

I think cljs.proxy originated as a more efficient clj->js alternative, which is also recursive

nil and null should be the same