What about common lisp

Hi there,

Clojure is my number one productivity tool, I did my thesis work with it, I make money with it, I make scripts with it (thank you babashka). I love the langage and its philosophy.

But, once in a while, I try to learn a new language. In fact this is how I met Clojure, when I was a little scala-sold college student. And I was wondering about learning Common Lisp (CL).

AFAIK, Clojure has strong roots in CL. I guess there is many lispers in the Clojure rear guard. Many of you may have interesting insights about the two langages.

I saw the talk of Alan Dipert Common Lisp for the curious clojurist where he pinpoints the fact that Clojure’s killing feature is the consistent philosophy that Rich Hickey put in the langage. This brings idioms that make working together in Clojure easier. Such idioms does not exists in CL, so you have more room for doing stuff the way you want, which is a feature but also a sort of community tragedy.

What are your opinions about common lisp ?
Would you consider it for doing real work, as a hobbyist ?
Do you think it has major pitfalls that would make Clojure a best fit for you ?
Do you already use CL from time to time for specific tasks ?
Will I feel disappointed and clojure-nostalgic after using it for a while ?

Thank you very much for your time.

1 Like

I did the reverse route, went from Common Lisp to Clojure. Common Lisp is a great language, totally worth learning. A lot of features are very sophisticated: CLOS, exception system, etc.

Is very different from Clojure, bigger, very different design choices, so is an interesting language to study. But it will be familiar enough coming from Clojure.

On the plus side, I miss the performance of SBCL (the implementation I used)

On the other side, I moved to Clojure mainly because Java libraries. You will miss massive Java community libraries.

My recommendation: do it!

3 Likes

Me too. CL is great, but it’s kind of a mess, and in a weird way, that’s a source of its quirky beauty, and certainly of its power. I love Clojure’s relative simplicity and consistency, and it forced me to learn and appreciate real functional programming more deeply than previously.

Maybe this was covered in Dipert’s talk–forgive me if you know this: CL is a mess because it was literally designed by a committee. Its predecessors were lisps that had evolved apart as researchers at different universities and companies added functions and features. So there are functions whose purposes overlap, but have different syntaxes. And there is less syntactic consistency across functions that do different things than there is in Clojure. But because the designers wanted CL to support all of the different programming paradigms that lisp folks were exploring, you can do things in lots of different ways. It also means that there are lots of libraries, but sometimes there are too many to choose from, and some of them might not have been updated for decades, but you’ll still find them in a list of libraries that you have to sort through. The fact that it’s a lisp-2 rather than a lisp-1 means that some areas of functional programming are inelegant. On the other hand, it’s harder to make certain mistakes in a lisp-2.

None of those are reasons not to learn CL. And SBCL is great. You might want to start with a slightly friendlier Common Lisp implementation at the beginning, though. I like CCL for that, and Clisp is good for that purpose, too. Also, if you like an IDE, the free version of LispWorks is good, if that’s still available. After I got used to CL again (I’d played with it earlier), I used SBCL almost exclusively. (Proviso: I haven’t used CL much for several years, so there may be other good options.)

(A warning: Several years ago, when I was using CL, I tried ABCL, which runs in the JVM. At that time, it was slow compared to any other well-known Common Lisp implementations, and interop with Java was painful. Clojure by contrast makes some sorts of interop trivial. Maybe Java interop in ABCL is easier now, but my belief at the time was that it was difficult simply because of the different semantics of Common Lisp and Java. Clojure was built from the start with the idea of Java interop in mind, and many of the basic datatypes are more or less Java datatypes, so you don’t have to work around differences in semantics.)

2 Likes

Highly recommend “Let Over Lambda”, by Doug Hoyte, a book nominally devoted to Common Lisp macros. But macros are merely the atmospheric dust around which Hoyte conjures a thunderstorm of Common Lisp worldview. Brilliantly entertaining and edifying. No CL background required, if you’re seasoned in Clojure.

I can see how Clojurists might wonder about “returning to the roots” in Common Lisp, more or less the way Java programmers might think they would enjoy doing a bit of C to clear the cobwebs.

Start with the book! “Let Over Lambda” is the best-ever book for Lisp diaspora curious about life in the old country.

4 Likes

Hey thank you for the recommandation !
I just read first two chapters of Doug Hoyte’s book and it is definitely interesting.
However he puts a great focus on macros as the core tool CL has to offer.
In Clojure however, while we aknowledge the power of macros, we tend to use it really carefully, as a library API for example. But we don’t encourage people in the large to write macros everywhere, we state that functions are enough most of the time. I think this position is detailled in Joy Of Cojure and Clojure for the brave and true.

Based on what I understand from the various answers you gave me (thanks !), CL is some kind of a mess lang with everything you need to make the langage yours. Adding your macros seems reasonable because it becomes truly yours. Whereas Clojure is simple, pure and working, so adding your own complexity with your macros mess it up in some way.

Interesting, you all motivated me !

1 Like

I worked at several companies back in the day whose products were based on CL.
I’ve also spent the last 7 or so years working on a product in Clojure. So I have a lot of time invested in both, and they are both in my top 3 favorite languages.

I stopped using CL a long time ago because it lost momentum. That said, there are times I look back at it fondly when Clojure is driving me nuts.

In the end they both have things to offer. Clojure+JVM is great. Persistent data structures are nice and greatly facilitate FP. The Clojure language is still immature in various ways, and while it’s defmulti is flexible, I routinely experience compilation-related issues with it that I never had with CL. In fact, compilation is where CL shines, including some things that Clojure forsakes, like argument checking at compilation time, something you can get a fair amount of in CL. Clojure’s “maps all the way down” has drawbacks for production code, IMO.

CL also has great things to offer. CLOS and CL generics are a treat. CL’s LOOP macro allows easy mastery of the toughest loops, whereas Clojure’s iteration takes a while to learn.

If you’re writing programs that have a mutable flavor to them i.e. where it’s all about state, such as simulators, schedulers, and so on, Clojure can lead you down some tough paths. The tools are there, but it isn’t as natural as CL for some kinds of problems. On the other hand, Clojure offers a lot of threaded programming stuff (courtesy of the JVM) that was never officially standardized in CL (so … no future) in CL except as a vendor extension. Note that there is an open source package out there (name escapes me) that maps a standard set of CL parallel programming constructs across implementations, so it isn’t as bad as it sounds.

There have been some efforts to bring some of the nice things Clojure mainstreamed to CL, including threading macros and syntax for sets, vectors, and maps. In a tribute to CL, the reason they can do this is because of CL’s support for readtables, which is not a thing that Clojure does. Anyway, if you know clojure and want to play with CL, look for packages that bring your favorite clojurisms to CL and get some of the best of both worlds.

Some people think the standards effort made it a mess. I can’t argue against that, though it seems less crazy that CL has things like plists when you factor in the time at which the standardization was done. Personally I think a lot of very smart people worked very hard to dot all the i’s and cross all the t’s of a specification while coming from diverse lisp backgrounds. Remembering that adage about “compromise means no party is totally happy” I would say it was a success, not least because I can pick up the CLTL book and know that any implementation will have everything in that book.

Clojure didn’t have to go through any of that, and many of its i’s are not dotted and t’s are not crossed. Random trivial examples: clojure.spec.never-ending-alpha and incomplete ‘v’ functions, i.e. Clojure has filterv and mapv, but no v’s for other functions like mapcat. Not everything should be lazy.

I’m rambling. They’re both great. I use Clojure now because I really like having a JVM. I definitely miss CL sometimes, though I don’t know if I’d do a new product with it again due to lack of momentum. If only there were a CLTL3 :slight_smile: (Fortunately a large number of CLTL2 people have retired, and so hopefully won’t read that and cringe).

6 Likes

What are those?

Another drawback to using macros in Clojure is that they aren’t first-class citizens in the language, so they aren’t a great fit with functional programming. You can’t pass macros to functions (e.g. you can’t pass a macro as the first argument to filter). There are certainly situations where they make sense (e.g. when you need access to unevaluated arguments) but it makes sense that the recommendation in Clojure is to minimize the use of macros.

Basically it’s a situation where lein+java+clojure execute some defmulti dispatches that aren’t the ones in my active code. It happens only rarely, but when it does it’s pretty much an hour or three wasted before I figure out why things aren’t working. The solution is generally to blow away all class files, including things in my ~/.m2 directory.

I once found a bug in the clojure bug db that shared simlar symptoms, but that was years ago and I don’t remember where it is, and I’m not sure it was describing the same underlying bug.

Sorry, can’t be more specific, it’s been a while since I had to deal with it.

TL;DR; if your defmethods are doing inexplicable things, scrub your compiled env clean (requires more than lein clean). Of course “compile” is a pretty strong word for some clojure kitting, where all it does is add source files to a jar file even if they could be completely uncompilable.

Clojure is a great language, hats off to the developers and maintainers.

I have used CL for many years. One thing that I like about the language, which I consider an advantage over clojure is that it is well specified. For most questions about CL, you can read the specification and understand how it is supposed to work. If you have a CL implementation which does not behave like the spec says, it is considered a bug, and the maintainers will(should) fix it.

Nothing is perfect, there are indeed errors in the CL specification. These errors are usually very exotic.

It seems to me that Clojure is specified by its implementation rather than implemented by its specification. That makes it difficult sometimes to understand how a particular feature really works.

I really prefer the package model of CL to the namespace concept of Clojure. The model of CL gives much more flexibility to the programmer. The Clojure model forces consistency across different users and different projects. Both have their advantage of course. CL has no distinction between symbol and var, which makes CL macro writing much easier the Clojure macros.

That being said, CL is really dated, and suffers from the fact that it was specified in the 1980s, and many of its assumptions are no longer valid.

Since Clojure is a Lisp, many (but not all) of CL’s abstractions can be implemented in Clojure. E.g., I regularly use cl-format from clojure because it lets me print in many different ways and has fewer gotchas that println seems to have, at least from a CL programmer’s point of view.

CL is worth learning from a historical perspective, and understanding some of its concepts can make you a better Clojure programmer (or a better Scala programmer or a batter x programmer, for any choice of x).

5 Likes

One point against a hard specification is:

When you specify something, especially such a robust specification such as CLs, you set it in stone. You need a committee to move that mountain.
Clojure, being more open by its design and nature, can be said to be specified by behavior, and not implementation. By behavior I mean the basic properties of collections, concurrency data types and behaviors, and passing the tests in core.
Also interested in how packages differ from namespaces and what sort of organization they enforce

2 Likes

I came from a CL background: CL was my primary programming language before Clojure.

I wrote a detailed post on Quora a while ago (https://qr.ae/TUnsIT), so I won’t repeat everything here. The post is from 2013, but most things still stand (except that Clojure has improved quite a bit since then).

I would not consider going back to CL.

2 Likes

One annoyance with Clojure name spaces is that for the tools to work, you have to put all the functions in one namespace in a file of the same name. In Common Lisp there is no connection between name of file and package (or packages) manipulated in that file.

You can make this work in Clojure, if you are willing to do without all the tool support. In Clojure you can use in-ns liberally within a file.

1 Like

You say “set it in stone” like that’s a bad thing. It’s a good thing. It means you can pick up a CL book and write CL code in compilers across a wide variety of architectures. Or you could back in the day. And the robust and reliable tools running on CL now owe their longevity and stability to that “set in stone” specification of what Common Lisp is.

There’s nothing preventing a new improved version of the CL language except that there is nobody sufficiently interested in it to finance it without a better ROI. Just look at the many editions of C++.

Contrast with Clojure which is particularly abhorrent from a stability standpoint. For example, the 100+ production files I had to edit when they renamed clojure.spec to clojure.spec.alpha, and the many freely available libraries (yay!) that pay no attention to backwards compatibility and break code in subtle ways when you upgrade (nay!). Describing Clojure as being defined by behavior sums it up well, but as a production language goes that has drawbacks.

1 Like

In my opinion, and what I tell my students, is that you take the good with the bad, unless you want to write your own language from scratch. Bravo to people who do that.

1 Like

One comment in that post is about bugs in CL implementations. I don’t know which bugs you’re referring to, but in many cases the reason you know they are bugs is because the spec tells you what behavior to expect. In Clojure when something behaves counterintuitively, you cannot really know whether it is a bug or if that is just the way it works, because the language is specified by its implementation.

Operating without a specification admittedly has certain advantages, especially in terms of agility.

1 Like

I don’t know which other languages you’ve used but Clojure and its ecosystem is a beacon of stability by comparison to most other languages!

I’ve been using Clojure in production since 2011 and we have always been able to upgrade Clojure – and run alpha and beta builds in production – with almost no code changes across that whole decade.

As I recall, clojure.spec was introduced during a prerelease version of Clojure and became clojure.spec.alpha before that version of Clojure was actually released, so only really early adopters were “hurt” by that – and we were a very early (and very heavy) adopter of Spec, but it was a one-off change and a simple, mechanical find’n’replace.

Community-maintained libraries are also much better about backward compatibility in general than in other languages. We rely on a lot of Java libraries at work and it’s pretty common to hit breaking changes in minor version updates (or even in patch version updates). We have a daily task that checks for outdated libraries and we rarely have problems updating Clojure libraries and keeping them on the most recent version – but we have had all sorts of problems with Java libraries over our decade of production Clojure work.

7 Likes

I’ll clarify my statement. Clojure, to the extent it is a language with a compiler and clojure.core definitions are its runtime, has been pretty stable, so kudos to the folks behind that.

My ‘abhorrent’ comment is more about some the libraries that I use with it. I don’t want name them and single out their owners, but there are some some packages I have used for my work that are just cringe inducing every single time I upgrade, while others are stable.

Whether it’s a general cultural shift or a clojure-specific one I don’t know. While I spent a long time writing Java code, my use of third party code was smaller there (so more of it was developed in house). Once Maven started making things easier for using third party code, it also made things harder. Some dependency of a dependency would be upgraded and then cause product failures.

Anyway, I’ll walk back my statements on Clojure, proper. It’s been a good if occasionally immature development language. However some some of the libraries I use with clojure show a decided lack of commitment to stability. If you’re of an academic bent maybe it doesn’t matter. If you’re shipping product to customers, it matters.

Of course there’s the whole “you get what you pay for” thing. Anybody shipping common lisp products in the 80’s and 90’s was probably paying for a supported lisp product, and there was a standard, and the bar of expectations was generally very high.

3 Likes

I’m sorry you’ve had bad experiences with some libraries. Rich has tried to make it clear that breaking changes are just bad and I think folks are taking that much more seriously nowadays.

When I inherited org.clojure/java.jdbc (originally clojure.contrib.sql), it had a very problematic API and I did make breaking changes across several releases before getting it to a modern, idiomatic, “best practice” API and it has been stable since then. I created next.jdbc as a “next gen” version of that library rather than introduce breaking changes. Same with HoneySQL: V2 can be used alongside V1 because it uses a different library name and different namespaces – which is what Rich says we should do if we want to change the API in what would otherwise be a breaking way.

If I had my time over with java.jdbc, I would have introduced new namespaces for the new API so folks could have upgraded more easily.

3 Likes

Why not call them out. It can help others know what to expect with those libraries, and it could inform their maintainers that they might want to be more careful with breaking changes if it causes real pain to their users.

2 Likes