Skip to content

Instantly share code, notes, and snippets.

@vindarel
Last active October 12, 2024 22:28
Show Gist options
  • Save vindarel/3484a4bcc944a5be143e74bfae1025e4 to your computer and use it in GitHub Desktop.
Save vindarel/3484a4bcc944a5be143e74bfae1025e4 to your computer and use it in GitHub Desktop.
Notes on Common Lisp VS Clojure

Testimonies

CL's compiler

The thing in CL I miss most doing Clojure as my day job? CL's compiler. I like having a compiler tell me at compile time about the mistakes I've made. Bogus arguments. Unreachable code because of unhandled exceptions, and so on. CL saves me round after round of bugs that in clojure aren't found until you run the code. If you test well, it's found when testing, if you don't it's found in production. "Clojure compiler" almost demands air quotes.

CL's optional but oh-so-useful model of type declarations is also infinitely more useful (to me) than Clojure's use of "spec", and instrumentation that happens only at test time because of the cost. Depending on the OPTIMIZE declarations, other type defs are a floor wax and dessert topping. Want checks for argument types? Lower optimizations. Want most efficient machine code? High optimizations.

/u/Decweb, March 2023 https://www.reddit.com/r/lisp/comments/11ttnxk/the_rise_fall_of_lisp_too_good_for_the_rest_of/jczpysp/

Interactive development

I have semi-frequent occasion to work in Clojure, but Clojure lacks some features of Common Lisp that I consider important. The most important of these is that Common Lisp, like Smalltalk, but unlike most other languages, including Clojure, is designed from the ground up to support developing a program interactively while it runs. I want to see if I can have Clojure for those cases where it's the right tool, without losing the programming-as-teaching features of Common Lisp that I miss when working in other languages.

I know someone is probably going to object that Clojure, too, is designed to support interactive development; my answer is: no, it isn't. Not in the sense I'm talking about.

As an example of what I mean, consider the standard Common Lisp generic function UPDATE-INSTANCE-FOR-REDEFINED-CLASS. This function is part of a standard protocol in Common Lisp that enables your program to automatically update existing instances of previously-defined classes to properly conform to a changed class definition.

As I've observed elsewhere, the existence of this standard function is not an accident or an eccentricity, even though it makes no sense in most languages. It makes sense in the context of Common Lisp because the normal mode of development is for your work in progress to be running while you work on it, and no experienced Lisper would expect to have to restart the program or reload code just because they redefined a class. The designers of Common Lisp made this protocol a part of the language standard because they took for granted that the normal way to work on a program was to alter its definitions while it ran, and took for granted that you should reasonably expect the program to continue to work properly while you were doing that.

In other words, the language is designed from the ground up to support interactive programming. Smalltalk is designed with a similar set of assumptions. Most other languages, not so much.

https://www.reddit.com/r/lisp/comments/e1jr8b/cloture_clojure_in_common_lisp/f8ql96b/

No need to restart long-running processes

If for some reason something failed in a long running process, I can fix it in place and restart it from where it was [in Common Lisp] while in Clojure I would have to rerun it.

charlesHD on reddit (2024)

NOTE: I (vindarel) have a nice video about this: Debugging Common Lisp: how to fix and restart a program from any point in the stack.

"the tooling for most CL implementations is just plain light years ahead of Clojure"

For Clojure the interactive debugging experience is just plain dreadful and for a dynamic language this is pants on head crazy imo. For me a dynamic language has to have a good interactive debugging experience because you have foregone the support of a static compilation and instead wish to reason about your program at runtime. But with the JVM stack traces and lack of interactive debugger Clojure just does not support you in this regard. And as a side note the lack of an identity print is annoying as well e.g. (+ (print 4) (print 4)) "4" "4" 8 (yes, it's easy to add...)

Clojure's decision to use persistent data structures is good but it's really the only standout thing for me other than some syntax sugar like hash maps.

from https://news.ycombinator.com/item?id=13981261 (2017)

A question about it:

I have been led to believe that Clojure can also do interactive development. Certainly, with Figwheel it is possible to do interactive development. But with Clojure, I'm not so sure. What is the story please?

mikelevins:

It's certainly possible to develop interactively with Clojure. If you're used to Common Lisp, the environment will feel clumsy and incomplete. If you're used to figwheel, you'll miss some of the nice reloading and debugging conveniences, but interactive development with vanilla Clojure is certainly possible, and indeed good enough that some people like it (and some might take offense at my lukewarm praise).

Neither Clojure nor ClojureScript is in the same league as traditional Lisp and Smalltalk environments, but if you're not already used to those, you won't miss them. If you want to develop interactively and you want to use Clojure, you can certainly do it.

https://www.reddit.com/r/lisp/comments/q6pou3/notes_on_common_lisp_vs_clojure/


On debugging and performance:

I use Clojure at work but wow do I miss just about everything about Common Lisp whenever I have to debug anything or want performant code. Being able to be in nested errors and click at any part of the stack to inspect lexical bindings is extremely useful, and more importantly, clicking on an object then pushing M- to copy it to my REPL is much nicer than what Clojure offers (tap>, which I consider a glorified pretty printer even if you use tools like Portal).

As for performance, well, Common Lisp lets you statically type things, and SBCL can emit really efficient code if you do this. I find it helpful to run DISASSEMBLE on my own code to see what exactly is being emitted and optimize from there. And more importantly, packages like SB-SIMD and Loopus are a god send for any number crunching application.

from koito17, https://news.ycombinator.com/item?id=36887792, 2023.

or

As a Clojure dev, break loops and REPL-driven workflows sound wonderful, and something we could definitely benefit from, which would make it more like front-end coding with JS/TypeScript using the browser’s awesome debugging tools. Sadly, the state of tooling and community support for the Clojure ecosystem seems to be pretty lackluster at present.

from temporallobe, same HN thread

Development speed

I'm faster in CL despite having spent the last 10 years doing clojure exclusively.

After having used both lisps for many years, I've recently been enjoying CL again after Clojure. I can write very clojure-like stuff in CL if I want to (e.g. transducers, immutable data structures, etc), but not so much the other way around. And some problems such as nested loops and tree implementations I find much easier to write in CL

decweb, 2024, reddit

After looking at the quite old benchmarks, ABCL seems to perform alright. Can anyone share their experience with ABCL in terms of performance, stability and memory usage?

I wish I could give you more concrete numbers with an application you could test and see for yourself. Since I can’t do that, I will tell you about my recent work with ABCL. I ported a small Clojure server-side utility to ABCL and can qualitatively tell you that the performance was close to Clojure. After profiling the ABCL version, I believe I can attribute the differences to ABCL’s use of Java reflection for its Java FFI.

I’ve already been successfully deploying Clojure-based applications professionally, and as I’ve gotten more into Common Lisp, I’d like to start deploying Common Lisp based applications as well. I recently posted a patch to the ABCL mailing list and got a very quick response from the maintainer. I really like the quality of the ABCL code base. The compiler itself was very approachable and easy to understand.

I think ABCL really is a worthwhile target in the Common Lisp world because:

  • Painless Java FFI. You avoid all the instability and signaling issues that crop up when using the JVM combined with SBCL or CCL. If you make a lot of calls, native Java is always going to be faster anyhow than calls over JNI (which is more comparable to reflection).
  • Use large heaps without worry. Part of the benefit of the JVM is its proven ability to have huge heaps (I’ve been part of projects that had 64GB+ heaps (though honestly I’d rather stay small)).
  • JVM platform is well supported and tested on a number of OS and hardware platforms

SBCL uses conservative garbage collection and I’m curious how well it would handle really large heaps. CCL uses precise garbage collection but again, I’d like to know how it handles really large heaps. In general, I want all my applications to run with heaps that are naturally in CCL’s or SBCL’s sweet spot, but I’d love to know I could use ABCL if I really ever needed huge heaps. I’m really getting into Common Lisp because I really like the implementation choices. Having a solid Java FFI unfortunately is usually a requirement in my workplace.

To me, ABCL will be /better/ than using Clojure if ABCL’s Java FFI moves away from reflection (when possible). This will close any performance gap with Clojure for most applications. I think this can be done relatively easily in the current ABCL implementation, and I have an idea of how to do it but unfortunately have had no time lately to devote to it. The reason I say “better than Clojure” is that I can write applications that target both ABCL and SBCL/CCL – I can abstract away my Java APIs if I really have to have them (or use LispWorks with a solid Java FFI if I don’t need a ton of Java interoperability). Then when I need fast startup time or low memory footprint, I can use these other CL implementations which are much better suited to it.

The main benefit where I still see Clojure having an edge is if you need a heavy JS-based web interface. I’m not a JS developer, but I was able to successfully use Clojurescript and make a nice looking web application that had pretty seamless interoperability with my Clojure-based server.

Anyhow, I hope this helps you. ABCL is great, I have been very impressed with it and I encourage you to try it out.

from https://www.reddit.com/r/lisp/comments/d48gcr/how_well_does_abcl_perform/

Thoughts on learning Clojure and Common Lisp in 2019 (and some other stuff)

Clojure: It’s a really cool language with a stable core — but there are some issues that make it difficult to get into and sometimes it feels hard to do what you want to do and slower than you would want it to be.

Common Lisp: CL is very fun to program with. It’s very stable and mature ecosystem. If you use sbcl + portacle getting started is basically 5 minutes. I definitely recommend common lisp as an entry point to lisp.

https://medium.com/@yvanscher/what-have-i-been-up-to-for-the-last-few-months-of-2019-5997b632876b

"If you like CL, try Clojure -- all the Lispy goodness, but with access to modern, industrial-use libraries." (or really?)

I’ve been working with Clojure for the past 8 years, and while I can vouch for it, I wouldn’t say it has all the Lispy goodness of CL:

  • it lacks a robust debugger (where you can step, redefine, continue…)
  • it uses the GC of the host VM
  • can’t save interpreter images
  • is designed to be a hosted language, meaning many implementation details are left to the host VM (JVM or JS)
  • type checking
  • performance

hcarvalhoalves, 2024-05 https://news.ycombinator.com/item?id=40408915

"I actually switched from Clojure to CL."

I switched because I was making command line tools and clojure's startup time is abysmal. Babashka doesn't have all the libraries and GraalVM's native image requires herculean effort to work even a little bit and spits out monstrously huge binaries.

Common Lisp is actually a lot more popular than I thought it was. I think perhaps it doesn't look that way, but it's very strong I assure you. Join us on the lisp Discord!

It is possible to create relatively small binaries out of the box with nearly instant startup time. There's plenty of libraries and a strong community. There's also a great number of implementations.

A lot of the things that are not standardized in the standard have been standardized via implementation agreement and libraries abstracting away different implementation differences, such as cffi and bordeaux-threads. I defy clojure to have anything nearly as feature complete for parallelization as parallels, blackbird, and CL-async. Speaking of which, the C FFI story is strong enough with lisp that any major missing library or language feature usually has a wrapping list Library around that feature written in C. While it's not standard, all the major implementations have adopted gray streams and people use flexi streams to open sockets. It's the same kind of du jour standard that Clojure relies on.

The standard is flexible enough to have several different implementations on several different platforms. If I need to interface with JVM languages, I can use Armed Bear Common Lisp. If I need just a really strong implementation that works in most situations I can use CCL or SBCL. If I need to integrate heavily with C or C++ code I can use ECL or CLASP.

It is clunkier because it takes the view that agreement between programmers is more important than clean syntax. The standard was created to unite the different lisp implementations instead of divide them. I value this view.

djha-skin, 2024-05 https://news.ycombinator.com/item?id=40408616

Clojure annoyances (2016): memory hog, slow startup, deal with Java, awful backtraces, not always expressive, heavy setup, docstrings require an extra line

Every time this is brought up there are arguments that Clojure and the JVM alone don’t use too much memory. But once lein is starting an nREPL for a non-trivial project, you’ll want to have a 16+ GB machine. The situation would be even worse if you use a heavy IDE (like IntelliJ or Eclipse). […] So if you’re not careful, you’ll want to limit yourself to running one project at a time.

Clojure starts up quickly (relative to lein, anyway). But the full lein environment is taking me 17+ seconds in a default Luminus project.

http://www.micahelliott.com/posts/2016-12-03-clojure-annoyances.html

When should I choose Common Lisp over Clojure for business (or vice-versa)? (2022)

"If you expect to integrate with others' libraries, such as deliver a project to a customer - Clojure might be more pragmatic since it runs on JVM..." (answer:) "SBCL, ECL, LispWorks, and Allegro all allow delivering a shared library, so your Lisp code can roughly look like a C API to the outside world. (It's not 100% straightforward, but it definitely works.)"


"Have you found it difficult to find freelancers/full-time employees?" "No issues finding freelancers, just go to #lisp on IRC. Local full time employees will depend on where you are based. You can always train those with no CL experience. At one time I trained 6 interns at once to code in lisp in a couple of months."


"Common Lisp is really, really good if you know how to program it quite well, and aren't too distracted by deep philosophical thoughts about the nature of programming that Lisp sometimes has a reputation for providing.

For hiring other programmers, you should be ready to train them and review their code. A good programmer will pick up Lisp and be very productive with it in less than a month.

Except for a small select few things, Lisp code almost never breaks. The ecosystem, while imperfect, is extremely stable. Use Common Lisp if that's valuable to you.

Lisp is also good at letting you develop extremely chiseled applications, ones that need to be fast, flexible, and no-nonsense. Deployment isn't hard, but it's also quite barebones and you'll have to use some elbow grease to get a good deployment setup.

On the other hand, if you're going to be doing a zillion integrations with other software and services, having Java at your disposal, while incredibly boring, might be a lot more advantageous. I don't personally recommend Common Lisp for "just" gluing things together; I recommend it when you and/or a team you hire really need to roll up your sleeves and program something."

https://www.reddit.com/r/lisp/comments/wn7qhr/when_should_i_choose_common_lisp_over_clojure_for/

CLOS is the best object system

I should explain myself better, I spent three years of my professional life implementing the same object oriented system in all three of Common Lisp, Java, and JS. The CL version was the shortest, had the fewest bugs, the most features, and the best performance, and was the easiest to extend and maintain. This was because CLOS was the best object system of the three.

Don't get me wrong. I love Clojure, it's the [censored] of these (and all) languages, and we use it all day every day to operate our business and it's super fun and lucrative. But Common Lisp's object system is the best object system.

CL's call-next-method, as well as multiple inheritance, are both powerful features that CLOS has that Java and JS do not. CLOS is an objectively more powerful object system, anyone who has used both can tell you.

hrrid, 2024, reddit


See also:

Libraries:

  • clj-con implements Clojure concurrency primitives in CL.
  • Cloture wants to be Clojure in Common Lisp. Work in progress.

More resources:

@swapneils
Copy link

Thanks for collating this! I'm trying to learn both languages, and these kinds of analyses are very useful for determining how much to prioritize each given my personal preferences.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment