clojure 2026-02-23

@seancorfield we recently updated org.clojure/core.cache to 1.2.254 and apparently https://github.com/clojure/core.cache/commit/c15b60d09f417636b4e9239fbb421ef8ba4feb6f makes the cache store exceptions for keys, which didn't happen before. https://gist.github.com/jpalharini/d92656a06b281f0ba87c459b346f598c will store a cache entry for "throw" in 1.2.254, but not store it in 1.1.234. I'm wondering if this is actually the expected behavior considering we want to ensure value-fn/wrap-fn is called at most once, and evicting invalid entries is supposed to be done on the consumer side, or perhaps by implementing a "validating" cache.

😵‍💫 1

Interesting. That was not an intentional change in behavior -- but I will note https://clojure.atlassian.net/browse/CMEMOIZE-31 which was raised against core.memoize for a similar issue: exceptions are cached. Can you create an Ask with details and reference both CCACHE-65 and CMEMOIZE-31 and I'll have a think about solving both? (I was already investigating CMEMOIZE-31)

Sure! I'll do that by tomorrow at the latest.

Ah, it doesn't (technically) cache an exception, it caches a delayed fn invocation, and the invocation throws when fetched from the cache. Which is the same bug as core.memoize already had, because it takes a similar approach. I wonder if I can switch to some sort of promise/deliver implementation that doesn't deliver exceptions...?

Yep, it caches the delay. Sorry, I should've been clearer.

How many clojure.core functions/macros does clojure.core/println depend on? 1️⃣ 3 2️⃣ 12 3️⃣ 42 4️⃣ 85 5️⃣ 117 React with your answer! 👇

4️⃣ 4
3️⃣ 3

requiring-resolve: 166 is the largest

✅ 1

would be interesting to see a DOT graph of the dependencies 🤔

I could do that pretty easily, but so could you. Here's the data for you. https://github.com/gloathub/gloat/blob/main/build/clojure-core.yaml

👍 1

PS I'm fascinated by your surname. 😄

😄 1

böws döwn! 😂

I feel like the graph for the entire core library is going to be extremely busy.

Maybe just start with a dot graph for println

And if you gaze for long into an abyss, the abyss gazes also into you.

I think those are supposed to be various macros, but `clj-yaml` seems to parse foo/M as M I don't know how to write code.

So it ended up combining several ../M into one.

you can almost see something from this. perhaps I'll see if I can graph the dependencies of a particular function, if it looks any clearer.

@souenzzo in the https://github.com/gloathub/gloat/blob/main/build/clojure-core.yaml data I added /M to the macros and /S to the special forms

They could just be removed here.

I can make out concat on that middle one

Many macros use concat. Not sure if your algorithm consider ~@ as a concat.

I got the data by scraping the Glojure AOT code, where I think macros are expanded.

BTW -Xprune works now with 50% size reduction and 80% startup time reduction 🙂

YS's ys.std/say avoids clojure.core print calls but still needs to call apply and str and a few others

It's so nice to have a clear lens into this stuff. I think gloat -Xdeps will be super useful even to non- #glojure people.

it's a fun pop trivia quiz! take a guess!

❤️ 1

Afterwards I'll show you how many deps every cc form has 🙂

I'm not going to count!

😄 1

i actually ran into this when writing a formatter for Project Fluent files last year. I couldn't find the source of the the poor performance (vs a 1-to-1 version in java) until i tracked it down to calling pr-str instead of Writer/.write.

✅ 1

do you only mean first-order dependencies? technically printing bottoms out at print-method / print-dup which are multimethods (i.e. arbitrarily open to extension) so i'd say the answer is "it depends" 😄

I found out the answer originally when I was compiling a Clojure to Go code to binary file and wondering why it was so huge. See https://gloathub.org/blog/2026/02/22/introducing-gloat-and-glojure/ My source code only used println and some other trivial function like inc. I removed the rest of the code, but it was still a pretty huge binary after that.

> do you only mean first-order dependencies? No, I mean the recursive dependency tree. Unique functions/macros of that tree.

a winner is me!

🙌 1

@nbtheduke I honestly thought everybody would copy you. 😄

i was worried it was higher!

I'm working on a system to do some tree shaking on the AOT compilation process to go code.

Pretty much every binary executable is going to have a println. All it needs is Go fmt.Println For general stuff, not 85 extra functions.

Since most of my input is YAMLScript anyway, YS has say for println So I can just make that do the fast thing. Can't really mess with clojure.core/println

I guess for Clojure tree shaking purposes, people could just replace println in their ns with a function that does Go interop to fmt.Println

I should be able to get that done this week. gloat -Xprune foo.clj && ls -lh foo

Right now all the binaries are weighing in around 65MB. I think that's stripped but I'm not 100%.