Bending the CLOS Mop for Java-Style Single Dispatch

From 3‑hour boot to speed hack: Lisp dev rewires Java calls and the comments explode

TLDR: A developer turbocharged Clojure-on-Lisp by reshaping Lisp’s method chooser to behave like Java’s, slashing a painful startup delay. Commenters cheered the pragmatism, shared similar horror stories, and debated whether this is genius engineering or tech debt waiting to happen—either way, it’s a big performance win

Three hours. That’s how long it took to reach a Clojure prompt before one developer went full mad scientist and rewired Lisp’s brain to act like Java’s. In plain English: Common Lisp carefully checks every argument to pick a method, while Java just looks at the main object you’re calling. That mismatch made startup crawl. So the author hacked the dispatch system—basically the “who should handle this?” chooser—so it behaves the way Java expects, with a quick, class-based cheat sheet. The thread loved the audacity. One top voice, the author themself, flexed: they got a Clojure REPL (that’s the live coding prompt) running on a Lisp-based Java engine. Another reader chimed in with war stories: their Smalltalk-on-Lisp experiment sprinted at first, then face-planted as classes piled up—exactly the pain this fix targets. Cue the memes: folks joked about “the 3‑hour REPL challenge,” coffee breaks long enough for a road trip, and a CLOS vs. Java steel-cage match. The strongest opinions praised the “do what works” vibe—bend the tool to the problem, not the other way around. A quieter undercurrent wondered if this is brilliant engineering or a maintenance nightmare in disguise. But for now, the mood is clear: bold hack, big win, and serious nerd brownie points

Key Points

  • Clojure startup on OpenLDK took nearly three hours due to CLOS multi-method dispatch costs mismatching Java’s single dispatch.
  • SBCL’s PCL dispatch pipeline (dfun, compute-applicable-methods-using-classes, effective-method) repeatedly rebuilt nets during rapid class loading.
  • Java requires single-dispatch keyed on the receiver class; a cache per receiver type is a standard optimization.
  • A new metaclass (java-generic-function) was implemented with hash-table caches for virtual dispatch and invokespecial, plus a thread lock.
  • The discriminating function was replaced via the CLOS MOP so Java method calls bypass multi-dispatch and use receiver-class caches.

Hottest takes

"I managed to get a clojure REPL running on Common Lisp" — atgreen
"Worked great at the start and then performance dropped off" — wild_egg
Made with <3 by @siedrix and @shesho from CDMX. Powered by Forge&Hive.