Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

First, you should not abbreviate Common Lisp as CLISP: this is confusing because CLISP is one of the many implementations of CL.

As for comparisons, this is a Gorilla vs Shark problem (https://blog.stackoverflow.com/2011/08/gorilla-vs-shark/), without context you can choose anything you prefer.

LFE is not really Lisp, it is more like Erlang with parenthesis, so it is hardly comparable. Erlang offers a very specific, and useful, model of computation: if you need that, use Erlang or LTE.

In CL vs. Clojure, I suppose CL looks riskier to use than Clojure, w.r.t. all those JVM libraries (and the coolness factor). I think this is false, because you can access Java/Clojure/C libraries from CL with ABCL/CFFI, whereas you cannot access the Common Lisp ecosystem from Clojure easily.

But in the end, which programming language you use depends on many factors.



Let me try to rephrase my question: I know what I can use Clojure or LFE/Erlang for and what their strengths/weaknesses are. I don't really know anything about Common LISP other than it is a LISP, so I would love to hear some factors that led some people to prefer Common LISP to anything else out there (other than personal preference, which is always a factor).

Is it good at number crunching (which is not Clojure's and definitely not Erlang's forte)? Is it good at low level systems programming? Is it good for computers with low memory resources? Etc. In your words, I am looking for the context.


That's a very complicated question. Common Lisp has many implementations, with different strengths and weaknesses. For example SBCL and CCL are good general purpose implementations. They're fairly performant, especially SBCL, properly optimized code can compete for C-like performance. ABCL gives you Java integration, not as nice as clojures for example, but perfectly usable IMO. ECL has a fairly small footprint and is good for embedding, it can also compile down to C I think. Clasp is a newcomer to the Common lisp world, it compiles to LLVM and provides C++ integration. It's creator uses it for chemistry and nanotech simulations, so he has many C++ libraries he wants to use with Lisp, so he wrote an implementation. There are also commercial offerings(LispWorks, Allegro CL) that come with additional support and their own graphical IDEs.

When I switched from Clojure to Common lisp a few years ago, mostly I really like using multi-paradigm languages. Clojure code wants to be functional. Common lisp code has no such preference, and with the excellent OOP support it's a pleasure to use. Basically it's generic function based, rather than class based, which means that your methods are owned by your generic functions, rather than your classes. This gives you much more flexibility and power than "standard" OOP. I can write software in the style that best supports the problem I'm trying to solve. Other great features include the condition system, basically exceptions on steroids. Very few languages have something similar.

Another one of my favorite features is that the standard has been stable for decades. The core language hasn't changed since 1994, so the ecosystem continues to evolve constantly, but the core stays stable and it isn't unheard of for code written in the 80's to run with little or no modifications. I like the idea that my code might have that kind of longevity.

I could go on, but you get the general idea of having an extremely mature and stable language with many implementations that all try to solve different problems, with sufficient portability that fragmentation doesn't tend to happen(like in the scheme world for example). And personal taste of course, It's just a pleasure to use for some reason. I don't know why, but I'm just having way too much fun playing with it and trying to explain it to others.


Speaking as a longtime (nearly 30 years) Common Lisp programmer, I agree with what you've said here, but I would add one thing more. Common Lisp is one of a small number of languages that represent an approach to programming that is so out of fashion nowadays that it seems to be in danger of becoming extinct. The approach is one that treats programming as an exercise in interactively shaping a living thing in real time, rather than building an artifact from a blueprint. Programming-as-construction is the mainstream; Common Lisp and a small handful of other languages represent a different approach that we might characterize as programming-as-teaching.

Common Lisp and some older Lisps, along with Smalltalk and FORTH, treat programming as an ongoing interaction in which your main program is already running before you ever write any code. It's running, but it doesn't yet know how to be your application. You have to teach it. This you do incrementally, one small change at a time, gradually guiding and shaping the running program until, eventually, it becomes your application. It continues to run throughout the entire process; it just changes its behavior as you teach it new capabilities.

Common Lisp (and a few other languages) are designed with this approach to programming squarely in mind. Consider, for example, the standard Common Lisp functions CHANGE-CLASS and UPDATE-INSTANCE-FOR-REDEFINED-CLASS. In most languages these functions make very little sense. Once you understand how Common Lisp is designed to be used, it's obvious that they are necessary. They're necessary because obviously you're going to redefine classes as you progress, and obviously you don't want your running application to fail simply because you've redefined its types out from under it. So the language is designed to support that way of working. It provides facilities for gracefully handling the situation that arises when you redefine types out from under your code, and it gives you ways to ensure that the program keeps running as you make those changes.

This way of working is now sufficiently out of fashion that even new Lisps don't necessarily support it. Clojure isn't great in this area. Neither are some Schemes. Common Lisp and Smalltalk are the strongest remaining bastions of programming-as-teaching, as far as I can see.

Almost everywhere else in programming today, tools and languages uncritically assume the programming-as-construction model. Maybe that's okay. There's certainly good work being done in that mode. Programming-as-teaching is my native idiom, though. I'd guess that I'm somewhere around ten times more productive when working that way. That doesn't mean it's a better way, of course; it just means it's better for me. Nevertheless, I can't help but mourn when I think that maybe programming-as-teaching is disappearing from the world.


I wrote some similar thoughts here: http://lisperator.net/blog/why-lisp/ (in the "organic growth" section). Sadly I don't get to do much Lisp these days, but it remains my favorite programming language (CL, more exactly).

And yeah, it's sad that "programming-as-teaching" fades out of fashion. Now that I know what it's like, working in an environment that doesn't support it always leaves a bitter taste. Let's just hope more people try it, because it's addictive.


Interesting comment. I suppose it is along roughly the same lines as Paul Graham's essay about top down vs. bottom up programming (?) Read it a while ago, not handy on mobile right now.

What is it about CL / Lisp that makes people think it more suitable than other languages for using in such a style? It cannot be only the REPL, since others have them too. Or are CL REPLs more powerful? I've only explored one a bit, Franz, IIRC, some time ago. Edit: Okay, you gave two examples above, CHANGE-CLASS and another. Any other good ones?

Also can you elaborate with some examples on how the condition system is more powerful than other error handling approaches? I saw that mentioned here on HN recently, IIRC.


You're asking for a list of functions that takes interactive programming into account, and I'll give you one, but first I want to make the point that it isn't all about lists of functions or features. It's about a language and system being designed from the start with interactive programming in mind. If we focus on language features in the sense of grammar, syntax, and functions, we're likely to miss the point. You don't for example, turn a programming system based on programming-as-construction into one based on programming-as-teaching by adding functions. You need to design the system from that start with the assumption that you will be building your application interactively while it runs.

Common Lisp isn't the only language designed that way. Older Lisps were, too. So was Smalltalk. But that view of programming is now so far out of the mainstream that even new Lisp dialects don't reflect it.

But you asked for examples of other Common Lisp functions that take interactive programming into account. Instead I'll offer a mixed list of functions, special forms and design features. Don't assume it's exhaustive; there are too many to reasonably expect that I can list them all.

- COMPILE doesn't mean compile a file; it means compile an arbitrarily chosen function that might not even be in a file anywhere

- EVAL-WHEN enables you to control whether forms are evaluated at compile time, load time, or toplevel-evaluation time, because you might have program text that is intended to be evaluated at any combination of those times. Again, its behavior is independent of whether the affected forms appear in files (though obviously that's the most common place for it to appear).

- namespaces are represented by packages, which are runtime objects that you can interactive inspect and modify. The namespaces represented by packages have a concept of public and private names, but you can't really hide private names, because if you could it would be impossible to change them at runtime. Common Lisp is allergic to features that are impossible to change at runtime. It has to be in order to support interactive programming.

- CLOS has a whole protocol for changing the definitions of classes and functions while a program runs. CHANGE-CLASS and UPDATE-INSTANCE-FOR-REDEFINED-CLASS are members of that protocol. SO are REINITIALIZE-INSTANCE and SHARED-INITIALIZE and UPDATE-INSTANCE-FOR-DIFFERENT-CLASS and ADD-METHOD and REMOVE-METHOD and DEFINE-METHOD-COMBINATION.

- introspection is pervasive in Common Lisp. It has to be. It's an interactive system in which you build programs while they are running. You have to be able to examine every part of the running program as it runs. So it has scads of functions for examining the structure and value of everything in the running program--and not just examining it, but changing it. There's BOUNDP and SLOT-BOUND-P and SLOT-EXISTS-P and FMAKUNBOUND and FIND-METHOD and TYPE-OF and CLASS-OF and SYMBOL-FUNCTION and so on and so forth. DISASSEMBLE, which decompiles compiled code so that you can interactively examine what the compiler produces, is part of the language standard.

- the assumption of interaction is also pervasive. TRACE and INSPET are part of the standard. So is BREAK--a function that causes the program to enter an interactive breakloop in which you can examine the dynamic state of the suspended computation, inspect and change the values of local variables, redefine functions and methods, and resume the interrupted computation when you think you've made the changes you wanted.

- you asked about the condition system. You could say that it's Common Lisp's implementation of exceptions, and that would be true as far as it goes, but it would also ignore important aspects of its design. Yes, you can raise a condition, just as you can raise an exception. However, the normal behavior of an unhandled condition is to drop you into an interactive breakloop from which you can modify the suspended computation and all of its accessible state.Conditions provide restarts -- a mechanism for offering a set of options for resuming control at a place of your choosing, or of selecting how to resume execution under program control. Conditions are also more generally useful as ways to manage nonlocal transfers of control.

- it's not explicitly part of the standard, presumably because it was such a fundamental assumption of Lisp programmers when Common Lisp was being defined, but nearly all Common Lisp implementations support saving a serialized form of the dynamic run-state of the system that can later be reloaded to reproduce that dynamic run-state exactly. It means that you can save the exact environment that you're working in right now, complete with interactive state like breakloops and so forth, and resume it later. You can, for example, discover a bogu, dig into what you can find out by inspecting the dynamic state of a breakloop triggered by the bug, and save off that whole dynamic state, then reload it on another machine so that you can show it to a colleague in all its glory.

There's more, of course. It's hard to reduce a design that is all of a piece to a list of discreet features.


Thanks for the detailed answer; appreciated. Will reply again if any more comments after reading in detail.


I barely disagree. In that that 'way' is not dying, it's just that other languages spread the fundamental layer away just enough so that what people have for free in lisp/st/forth/ml takes a whole new organ (IDE) to reinvent flatly, but it's constantly reinvented.

These systems are so coherent they solve themselves in and out, but if you mess a few things it's now compounded interests fueling your technological debt.


Thank you for the detailed answer, I think you could expand on it and include it in your book as an introductory chapter, or even as a blurb (especially the second paragraph).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: