Two days ago I was in a funk- is ANSI common lisp really special, or am I just another person who thinks their language is the best? Am I… Just a retrocomputing enthusiast? No, it turned out I was even more correct than I previously imagined. Around then Amoroso pointed me at a few 2025 articles criticising ANSI common lisp, including a lovely brace of articles by budding ANSI common lisp enthusiasts Alex Nytpu and Dmitri Non.

Here I am going to try and explain what having a mature, currently used, popular programming language is like to and in the context of Dmitri Non’s and Alex Nytpu’s articles about being ANSI Common Lisp devs.
(Except for Classic Visual Basic, LOL! The dark horse. Micro$oft discontinued its work on the language in 1998 but I guess there is still an increasingly mature (less so than lisp) community!)
As I mentioned, I was filled with joy and hope for the future of ANSI common lisp by reading Dmitri Non and Alex Nytpu’s writings, prosaically named Common Lisp is a Dumpster and re: Common Lisp is a Dumpster (Jun 1, 2025). Both feel in their bones that they are ANSI common lisp devs, but are dealing with the familiar barrage of popular inaccurate or even misinformation often bandied about about lisp by recruiters for other programming languages.
I myself was in this situation, trying to rationalise how great the ANSI common lisp I was handling is under the weight of criticisms of lisp being slung at me. To the extent I am less confused about lisp these days anyway, thanks to a heroic effort by the lisp community at large.
Some examples of Nytpu’s and Dmitri Non’s are a good handful of stuff it would be easy to get wrong when non-lisp-users are constantly repeating urban legends to you.
I guess Norvig and Pitman’s Tutorial On Good Lisp Programming Style from the Lisp Users and Vendors Conference 1993 gets to the crux of the matter:
Common Lisp does not guarantee tail recursion elimination so it should not be used as a substitute for iteration in completely portable code.
(In Scheme it is fine.)
The Common Lisp do macro can be thought of as syntactic sugar for tail recursion, where the initial values for variables are the argument values on the first function call, and the step values are argument values for subsequent function calls.
do provides a low level of abstraction, but versatile and has a simple explicit execution model.
Allowing that ANSI common lisp has some (and find-if-not and loop…:thereis) so the following is not a real example, they say, do not write
(defun any (lst)
(cond ((null lst) nil)
((car lst) t)
(t (any (cdr lst)))))
but do:
(defun any (list)
(do ((lst list (rest lst)))
((null lst) nil)
(when (first lst)
(return t))))
Since it is going to be topical in a moment, let us look at these in terms of a prog form that performs them:
prog for the recursive any(I added an example list, (nil nil 1 nil))
(prog ((lst (list nil nil 1 nil)))
start
(cond ((null lst) (return nil))
((car lst) (return t)))
(setq lst (cdr lst))
(go start))
This prog captures what happens where in the tail-call-optimization style recursion, effected here using a go.
do for the recursive any(do ((lst '(nil nil 1 nil) (cdr lst)))
((null lst) nil)
(when (first lst) (return t)))
where we can see do absorbs the “tail recursive” update into the initialization of the lst variable, and its second arguement specifies the terminating base case of the recursion so the body of the do is not trying to unstructuredly define either of those duties.
We can see that the full story to our hypothetical antagonist’s lisp does not have Tail Call Optimization is that ANSI common lisp has a special macro, do or do*, where variables’ update rules are written with their initialization, instead of at the end of its body for example, and the recursion terminating base case is written in a destructuring list immediately after the variables and before the recursion’s body, which now does not have to have the base case and variable updates written somewhere inside it any more.
So ANSI common lisp is an especially excellent language for your beautiful tail call optimizations, once we get a little more into it.
We might have noticed that do and prog are a little bit similar. In fact, (pers. coms.) do was in the process of superseding prog at some of the usual lisp shops in the 80s. Let us write a more verbose form of any for our prog.
(prog ((lst (list nil nil 1 nil)))
(go start)
base-case
(return nil)
update
(setq lst (cdr lst))
start
(cond ((null lst) (go base-case))
((car lst) (go success)))
(format t "Did not succeed.~%")
(go update)
success
(return t))
Also note that in (format t ".."), t is a one-letter stand-in for “y’know, t o standard-output” like how “format nil” is “Not t o standard-output (i.e. return a string)”.
let us manually annotate upgrading that prog before to be do:
(do ((lst (list nil nil 1 nil) (cdr lst))) ;+A
((null lst) nil) ;+B
;; (go start) ;-B
;; base-case ;-B
;; (return nil) ;-B
;; update ;-A
;; (setq lst (cdr lst)) ;-A
;; start ;-B
(cond ;;((null lst) (go base-case)) ;-B
((car lst) (go success))
(t (go continue))) ;+C
(format t "Did not succeed.~%")
;; (go update) ;-B
success
(return t)
continue) ;+C
+A -A … initializing the variable lst absorbed the tail call update rule for lst+B -B … where we had used base-case … (go base-case) to let us put the base-case at the top of our body forms, do formally has a special destructuring list for our base-case before our &body.+C the “tail call” happens when we reach the end of our body forms. Since success is in the way, I needed to add (go continue) … continue to avoid the success return tag.Hopefully my messy, manual diff helped demo an upgrade from prog to do. I guess you noticed I was also illustrating that the body of do is also an implicit tagbody (i.e. allows the tags and go “prog feature”).
go to statement considered harmful (1968)In going with my theme of lisp’s unreasonable, incomparable time-depth of information attached to it, imagine if you heard someone say
goto considered harmful!
as a criticism of ANSI common lisp’s tagbody without providing e.g. this concluding 1978 summary from the lisp community (Sandewall in this case):
An additional reason for the reluctance in the LIsP community to discuss programming style may be a reaction to the debate about goto, which raged in the community in the early sixties. Programs without goto are written in LISP using recursive procedures and/or standard procedures with open procedural arguments, and a so-called “PROG feature” enables a restricted form of goto locally in a block. The controversy was resolved by a general agreement that the matter was not as important as it first seemed; this has discouraged subsequent discussion of other aspects of programming style.
Let me stretch you a tiny bit further before we draw our threads together.
https://mdhughes.tech (who understandably protested me refering to this popular “which programming languages are hot right now” index on the show) I promise I am making a good point and this list is important.
tl;dr basically LISP drops from hotness ranking #2 in 1985 to #9 in 2000 to #24 “hotter than COBOL” in 2025 (out of the top 100 languages each year). But let us graph how many years the current language used has existed, i.e. how many years of experience the language community and its compiler authors have with specifically the currently used language.

There are only two languages in the top 25 in which senior developers and the language’s development community have decades of experience: Lisp and Visual Basic (Classic). I got these numbers by plugging the language name from the TIOBE index into Wikipedia and subtracting Wikipedia’s latest release year for that language from 2025 (this year for me here and now). I guess Lisp without qualification refers to either Common Lisp or Interlisp (I read Kaisler say that): Wikipedia links to ANSI Common lisp first regarding programming languages called lisp, which I used in the graph. This is also the number of years the community’s language implementors have had working on the current language.
While other languages have kept their brand names, in Fortran’s case even longer than LISP, the language standard (if it has even been through a standards process) changes, the language implementations have to be changed by people newly unfamiliar with the target, the language name community has to start learning what-the-language-is-like-now-I-guess.
ANSI Common Lisp has the opposite of that. There are decades, and decades, and decades of computer science and software engineering experience and corrollary knowledge tied up specifically with lisp meaning the current ANSI common lisp.
An oddity of programming languages that are legal adults like Really Just ANSI Common Lisp is that there are tonnes of current language implementations purporting to conform to its current standard. sbcl (nee cmucl), Embeddable Common Lisp, Clozure common lisp, GCL (became conformant recently - formerly a cltl2 implementation), GNU CLISP, Strandh’s SICL, that classic Mac one I think nytpu has (?), Allegro Common Lisp (current, proprietary), LispWorks (current, proprietary), … . Having a diversity of implementors for one specific language probably comes with its maturity, but we only have ANSI common lisp for data for that at the moment.
Even though ANSI common lisp is absolutely titanic in developer experience and language implementation maturity with the current language itself, it does not change on you so over the years and decades, you keep learning and getting better with it, inheriting knowledge from your forebearers in the lisp community and contributing a little yourself. It is the only programming language like this. (Except for the discontinued-in-1998 Visual Basic Classic community! Rock on people.).
How many years into your own Lisp trajectory did it take for you to read me read you Sandewall’s summary of the 60s flame wars over Dijkstra’s Go To statement Considered Harmful? But now you have.
I believe that ANSI Common Lisp came to be like this because of Common Lisp: The Untold Story where Kent Pitman negotiated with ANSI that it was the lisp community, as such who owned ANSI Common Lisp, despite having been through ANSI’s standards process.
See you on the Mastodon as always for your thoughts and feedback. Once I patch up this week’s Lispy Gopher Climate recording, Kent has a great anecdote about TIOBE’s “lisp is more popular than COBOL” and more notes.
screwlisp proposes kittens