en en en pt es en en en en cv az en en en en en en en en en en en en en en en en en en en en en en en en en ca es en es ne en en en en en en en en en en en en en en en en ms en en en en en en en en pl en en en en en en en en en en en en en en en en fi en en en en en fr en en en en en de en en fr en en en en en fr en en en en en en en en en en en en en en en en en fr en zh nl en en en en en en en en en en en en en en it en en en en en en en pt en en en en en en en en en
LISP is a programming language designed by John McCarthy in 1958 while he was at MIT. It’s one of the oldest high-level programming languages. The name LISP is an acronym for LISt Processing, because linked lists are the core data structure of the language. After its inception, the language quickly gained traction in AI research circles because of its simplicity, its mathematical elegance and its expressiveness. Today, the term LISP usually refers to languages derived from LISP, the two most famous of those being Scheme and Common Lisp.
I’ve been exposed to Scheme and other LISP variants on several occasions during my studies. I will freely admit that I find LISP languages interesting and conceptually elegant. The Scheme philosophy is one of “less is more”. The language, at its core, is very simple, but provides powerful facilities for expanding the language itself, in the form of macros. Scheme is also very consistent and logical. Its design has clearly been given careful thought.
Scheme programmers seem to love the language. They praise its many qualities: its elegant syntax, its conciseness, its functional design, its expressiveness, the productivity gains it provides. It’s a language so powerful, it allows you to redefine the language itself through the use of macros. Scheme programmers love the language so much, they almost make it sound like strong AI would have been achieved years ago, if only everyone programmed in Scheme.
The question when it comes to Scheme and other LISP variants, however, is how come no LISP language ever really caught on? If everyone who gives the language any serious consideration loves it, how come so few people use it? Why are we stuck with so many flawed, inelegant, unnecessarily complex mainstream languages when there’s a much cleaner, elegant and expressive alternative right there in the form of Scheme?
Rudolf Winestock attempted to answer this question in his essay entitled The Lisp Curse. His answer: LISP is just too powerful for its own good. Its expressive power is its ultimate drawback. I don’t believe expressiveness is much of a downside. I agree with Winestock, however, that one of the real issues with LISP is the fragmentation of the community. In this post, I intend to look at some more concrete reasons why LISP languages, in particular Scheme, never caught on, despite their many strengths.
Newcomers might easily be scared away from the language before they even get to try it. They might try to find a Scheme implementation and find that it’s unmaintained and broken, or that it’s a pain to install on their system. They might also discover that the implementation they chose doesn’t fully support the latest Scheme specification, but rather some more-or-less conformant dialect of Scheme with undocumented features. You might think I’m just making up these scenarios, but they are actually part of my initial contact with Scheme. Although I haven’t tried Common Lisp myself, I believe it suffers from a similar problem.
Scheme follows a minimalist design philosophy. It provides a small set of constructs as well as macros, and the programmer is expected to extend the language to fit his or her needs. In theory, this sounds very nice. A minimalist, “less is more” type of design should mean less room for incompatibilities among implementations. It should mean the language is easier to implement and perhaps help it remain more semantically consistent.
The problem is that Scheme provides perhaps too little. The C++ standard library provides you with standard containers, such as hash maps and binary trees, as well as algorithms operating over those containers, such as sorting algorithms. It also provides basic support for multithreading and string tokenization. The latest Scheme standard (R6RS) doesn’t specify any of this, even though these are very basic features that many if not most programmers will need at some point.
Most Scheme implementations have chosen to extend the language to compensate for what the standard lacks. The issue here is that different implementations may implement these unspecified features in different ways. This causes further problems. Those who want to write Scheme code will often be forced to use non-standard features specific to whichever implementation they are working with. Sharing code then becomes problematic, because other implementations may or may not support the features needed. The limitations of the Scheme standard make it difficult to implement portable Scheme libraries.
One of the biggest frustrations I’ve had when trying to write Scheme code is the poor documentation. It’s not that easy to learn what you need to know about the language. Google searches will often yield irrelevant results, or results that are specific to one Scheme implementation. The official language specification document is probably the best source of documentation, which is rather sad. What if you need to use implementation-specific features? Well, the Gambit Scheme page has a long list of undocumented extensions.
Perhaps the Scheme community suffers from a lack of desire to document. From what I’ve seen, it’s fairly typical for Scheme code to have very few comments (if any) and be full of one-letter variable names. There seems to be a prevalent assumption that the code is self-evident and doesn’t need any explaining. This assumption is rather strange, because Scheme is probably the language that most needs documentation out there. In a language where you can use macros to (re)define language constructs, you should never assume that anything is self-evident.
Scheme is dynamically typed. It’s very much a dynamic language. Just like Python and JavaScript, Scheme allows you to dynamically change the type of variables at run-time. This makes it rather tricky to compile Scheme code efficiently. There’s been lots of research in the past few decades on how to implement efficient JIT compilers for dynamic languages. From the work on SELF, to Mozilla’s work on Tracing JITs, to all the work done on PyPy, to all the work that’s been done in the Java world.
I’ve got bad news for you. There are no JIT compilers for Scheme. Okay, there may be some experimental Scheme JIT out there, but as far as I know, all the major Scheme compilers compile code ahead of time. This is sad, because JIT compilers are an ideal choice for dynamic language. They allow you to take advantage of type information that’s only available at run-time. The Scheme community seems to think it knows better, and has decided to stick with static compilation, disregarding the last 20 years of research into dynamic languages.
By default, most Scheme and Common Lisp implementations use numerical towers to represent numbers with arbitrary precision. This makes it difficult to optimize Scheme code, because overflow checks need to be inserted into the generated code.  Non-integer numbers are represented by arbitrary precision rationals if possible, which make for better precision, but terrible performance. In order to get around this, special operators need to be used to operate on machine-precision integers and floating-point numbers. Scheme has taken the view that everyone should pay for the performance cost of arbitrary precision arithmetic, rather than only those who need it, because this is more mathematically elegant.
Some Scheme programmers seem to believe that Scheme is God’s gift to the world of programming and that it’s the right tool for every use. Don’t get me wrong, I like many things about Scheme (and other LISPs), but I’m a pragmatist, and I just don’t find it all that convenient to use in practice. I also fail to understand why anyone would think Scheme is a good first language to teach a new programmer. It may be mathematically elegant, but it’s certainly not the most intuitive language out there, or the best choice for new programmers, when there’s alternatives like Python.
My first exposure to LISP was in a first year undergraduate class on programming language semantics. In this class, we had to do the classic experiment of implementing an interpreter for a simplistic LISP dialect in Scheme. Most of my classmates, who had little programming experience, were rather confused. I was mostly confused because I didn’t really get the point. In order to understand the interpreter we were writing, you needed to already understand the level of language semantics the class was teaching. In the end, the interpreter in question was trivially simplistic and predictably inefficient. They essentially showed us how not to implement an interpreter if you care about performance to any degree. Talk about unrewarding programmer masturbation.
From → Programming, Programming Languages, Zeta
“They praise its many qualities: its elegant syntax, its conciseness, its functional design, its expressiveness, the productivity gains it provides.” This sounds like the words of many programmers, regardless of their language of choice. :)
LISP was always a language without a clear purpose. PHP is used for basic web development; C for performance; Java for making money; Python for its clarity and extensive, well-written toolset; JavaScript for browser development. Where does it leave a language like LISP?
Nicely done writing. :-)
“This sounds like the words of many programmers, regardless of their language of choice. :)”
IMHO, C++ syntax could be much more elegant, even after the advent of the new standard, Java isn’t a perfect piece also. I think C# is way better than the other two in this regard (unfortunately, I have no experiences with Python). The so-called OOP support in Javascript is rather ugly (not counting Brainfuck and the like, of course). I think, C++ is still relatively redundant, but still the most expressive language among these. Java is trying to keep up with C# in functionality. So I don’t think this statement stands for any language.
I have tried to learn and use LISP, but I have never got used to it. It may be elegant in some way, but on larger projects the code became very hard to read, understand and maintain, mostly because of the ocean of parentheses and the very deep nesting of expressions.
I had been thinking about to use LISP as a scripting language (like in GIMP) in one of my own pojects, but I have finally chosen Lua instead, due to similar reasons, and because the performance and convenience was important.
By the way, I have a fond dream about the perfect productive language. It is strongly typed, compiled, supports but not requires GC, supports reflection, it has functionality and syntax similar to C#, and has a similar class library to .NET BCL. C# is very productive, but the code produced by any of the current JIT compilers almost never reaches the performance that is provided by C++. CG and JIT does not allow to produce code that can guarantee response time (real-time applications, games). Moreover, interpreted languages always have limitations in performance, which becomes a concern in large projects again.
Have you tried Rust? It’s strongly typed (with inference for convenience), compiled, has optional GC, and has a lot of functionality similar to C#. However, its stdlib is still immature, and is deprecating things really fast, and I’m pretty sure it can’t do reflection, although if you really want to introspect program state you can directly manipulate the memory registers.
First the pedantic “It’s Lisp not LISP” (Sorry pet peeve)
More to the point: Lisp predates these languages. It did have a purpose: To be a language closer to expressing math and algorithms at a high level. Keep in mind that Lisp is old, so old only Fortran is older nowadays. Lisp was (and is?) the prototypical high level, programmer-friendly language in an era where C was still considered high level and slow.
“I have a fond dream about the perfect productive language”
Yes, seems promising indeed (but it still has avoidable GC, and of course, I don’t know how well does it work in this case). I’m curious about how developer support and practical applications will turn out with it. Currently, I don’t know if it worth the energy to start dealing with it or any other new language, it always takes considerable time and energy to become familiar with a new one, especially if the “good old” ones serve me quite well…. :-) I think I will see which way the cat jumps…
Rust is a better choice, but still not ready.
Recent lisp user/programmer permalink
> In this class, we had to do the classic experiment of implementing an interpreter for a simplistic LISP dialect in Scheme.
I’ve done this on my own not too long ago and found it enlightening although I’ve done it in C. ( https://bitbucket.org/knarf/zombolisp )
What you found “trivially simplistic” was a pleasant and rewarding experience for me.
Judging from your blog you probably know a lot more about it than me but optimization is a different problem and I’m glad you can demystify the concept of an interpreter in few lines of scheme albeit slowly. I don’t if you have read SICP (the MIT free online book mentioned at some point in any LISP discussion) but it’s one of its numerous exercices along with building a virtual machine (still in scheme) to optimize it in the following chapter. Scheme is just a tool with some nice starting point for the project :)
I’ll agree that the community is divided but you haven’t look hard enough. If you are still interested in modern Lisp have a look at Clojure. It’s byte-compiled (targets the JVM) and JIT’d. It’s very expressive (special syntax for sets, vector, maps, etc), has batteries included, OOP and a nice java FFI. Should be pragmatic enough for you :)
There’s also Racket (ex Dr. Scheme) which is byte-compiled + JIT and has some nice features. The REPL is especially great.
— written from Emacs (ELisp) ;)
Might it be that procedural thinking is more natural to us humans than functional? My own experience from programming is that it takes a certain fondness of mathematical logic and perseverance to uncover the full potential of functional programming – and after that there is no return to the old way of thinking. Functional way of thinking is very powerful regardless of the language you use…
I was under the impression that the better Scheme compilers already generate code which is much faster than any JIT for Python, Ruby, or similar.
The stock Python and Ruby implementations are incredibly slow. I’d be interested to see Scheme compilers pitted against Google V8, LuaJIT or IonMonkey on highly dynamic benchmarks though. I don’t think Scheme would fare too well, not without resorting to lots of special annotations.
I think Stalin would fare quite well. Chez Scheme also, but to a lesser extent
Racket is JIT-ted. There is a version of Chez that is JIT-ted. Kawa targets the JVM.
Targeting the JVM doesn’t count (i.e.: javac is a static compiler). Racket and Chez, I’d have to look into what optimizations their JITs do. If Chez compiles statically by default though, my guess is the JIT is mostly there to make an REPL/eval possible, and is basically a static compiler that runs on demand, not something conceived to take advantage of the opportunities JIT compilation offers.
The advantage of targeting the JVM is precisely that you get to exploit its JIT. Kawa does exactly what javac does: it generates bytecode that the JVM then executes with or without JIT. It also has a REPL in which the expressions you type are byte-compiled and then executed on the fly. With a modern JVM like HotSpot, this works very nicely.
The Java JIT is too low-level to be able to exploit Scheme-specific knowledge in optimizing it. It’s made to run Java. It doesn’t know much about Scheme’s typing, for one, only what can be approximated by Java’s typing. Like it or not, a Scheme to Java bytecode compiler can hardly be as efficient as a JIT tailored specifically for the Scheme semantics.
There’s Too Many Programming Languages! | Pointers Gone Wild
Leave a Reply Cancel reply
Enter your comment here...
Fill in your details below or click an icon to log in:
Email (required) (Address never made public)
You are commenting using your WordPress.com account. ( Log Out /  Change )
You are commenting using your Twitter account. ( Log Out /  Change )
You are commenting using your Facebook account. ( Log Out /  Change )
Notify me of new comments via email.
Notify me of new posts via email.
« The Indirection Problem
Inefficient Numerical Operators »
Typed vs Untyped Virtual Machines
Minimalism in Programming Language Design
Democracy, Dictatorships and Access to Information
Code that Doesn’t Rot
The Internet Changed My Life
What’s Could You Use Tesla Bot For?
NoiseCraft: a Browser-Based Visual Programming Language for Sound & Music
Art Artificial Intelligence Artificial Life Assembly Biology Compilers Creativity D Language Electronics Future Game Development Genetic Algorithms Grad studies Graphics Higgs JavaScript Microcontrollers Microprocessors Music Programming Programming Languages Psychology Rant Robotics Tachyon Technology Uncategorized work x86 Zeta
Archives Select Month June 2022  (1) May 2022  (1) March 2022  (1) February 2022  (2) January 2022  (2) December 2021  (1) June 2021  (1) February 2021  (2) December 2020  (1) September 2020  (1) January 2020  (1) November 2019  (2) October 2019  (2) June 2019  (1) January 2019  (1) December 2018  (1) May 2018  (2) February 2018  (1) January 2018  (1) December 2017  (1) October 2017  (1) June 2017  (1) April 2017  (1) January 2017  (1) December 2016  (1) November 2016  (2) September 2016  (2) May 2016  (1) March 2016  (1) February 2016  (2) January 2016  (1) November 2015  (4) October 2015  (1) September 2015  (3) August 2015  (2) July 2015  (3) June 2015  (1) May 2015  (3) April 2015  (2) March 2015  (1) February 2015  (1) December 2014  (1) November 2014  (2) October 2014  (2) September 2014  (3) July 2014  (3) May 2014  (1) April 2014  (1) March 2014  (1) February 2014  (1) January 2014  (3) December 2013  (3) October 2013  (2) September 2013  (1) August 2013  (2) July 2013  (3) June 2013  (2) May 2013  (3) March 2013  (2) February 2013  (1) January 2013  (3) December 2012  (4) November 2012  (1) October 2012  (1) September 2012  (3) July 2012  (4) June 2012  (2) May 2012  (2) April 2012  (4) March 2012  (2) February 2012  (3) January 2012  (6) December 2011  (2) November 2011  (3) October 2011  (8) September 2011  (4) August 2011  (3)