34

Does Lisp still have any special feature which has NOT been adopted by other programming languages?

By Lisp, I mean all the Lisp programming languages as a whole. I've been told how amazing Lisp is and know that many languages have been inspired by Lisp. But does Lisp still have any exclusive design feature that just cannot be done in any other language?

The reason I asked the question is that recently, being an amateur programmer myself, I began to learn Clojure just for fun, and the result is that I found lots of Lisp-related posts and comments, saying but one thing: "Lisp is unique", but other modern programming languages have already adopted and stolen lots of ideas from Lisp, like conditionals, recursion, and the function as a first-class citizen. And even metaprogramming can be done by many languages.

Did I miss out something and is "Lisp still different"?

Or I'm lucky because other modern languages have stolen all the good parts from Lisp so that it's not necessary to dig into the parentheses Lisp world, and "Lisp was different".

7
  • 3
    Sharing your research helps everyone. Tell us what you've tried and why it didn’t meet your needs. This demonstrates that you’ve taken the time to try to help yourself, it saves us from reiterating obvious answers, and most of all it helps you get a more specific and relevant answer. Also see How to Ask
    – gnat
    Sep 3, 2013 at 6:14
  • 3
    @gnat Thx for advice, and I've updated my question :)
    – iceX
    Sep 3, 2013 at 7:21
  • 10
    One problem is that, once a language has a certain subset of of Lisp features (for example, S-expressions and macros), people claim it's a Lisp. Which of course has the consequence that (according to these people) no non-Lisp language can have these features.
    – user7043
    Sep 3, 2013 at 8:51
  • 9
    I guess there is no other language where round brackets are the only form of grouping for everything :-)
    – Doc Brown
    Sep 3, 2013 at 12:11
  • Lisp as a family may be not terribly unique, but a lot of lisp dialects (Racket, CL, Scheme, Clojure) still offer a lot of useful/unique features. Sep 3, 2013 at 16:47

6 Answers 6

27

A canonical reference for this type of question is Paul Graham's What Made Lisp Different. The two remaining key features of Lisp that are not widely available, according to this article at the time of its writing, are:

8. A notation for code using trees of symbols.

9. The whole language always available. There is no real distinction between read-time, compile-time, and runtime. You can compile or run code while reading, read or run code while compiling, and read or compile code at runtime.

The commentary addresses each point and names popular languages where that feature is available.

8, which (with 9) is what makes Lisp macros possible, is so far still unique to Lisp, perhaps because (a) it requires those parens, or something just as bad, and (b) if you add that final increment of power, you can no longer claim to have invented a new language, but only to have designed a new dialect of Lisp ; -)

Note that this article was last revised in 2002, and in the past 11 years there have been a wide variety of new languages, some of which may incorporate all these Lisp features into their design.

12
  • 2
    Those features aren't unique to Lisp (and directly-derived variants), and haven't been for quite a long time. Sep 3, 2013 at 5:33
  • 21
    @iceX: The difference between JavaScript and languages like Lisp, Smalltalk, Self, Newspeak, APL, Factor, Forth etc. is that in JavaScript, programs are "dead". They are text files. You kill the running program, edit the text file, then start up a completely new copy of the program. In those other (so-called "lively" environments), you never stop the running program, the running program itself is a set of objects in memory that you manipulate the same way you manipulate any other object, while it is running. (Note: this is not true for Clojure, but most older Lisps do it this way.) Sep 3, 2013 at 8:25
  • 2
    @JörgWMittag Wow... So, programming languages like Clojure, Clojurescript, lispyscript are, in fact, not "real lisp" because they cannot change their code the way the old-school Lisp, like Common Lisp does. But... if they cannot change it... why bother to use the S-expression which I thought was for the purpose of code manipulation... (feeling like I just fell into a deep, dark rabbit hole)
    – iceX
    Sep 3, 2013 at 9:48
  • 4
    @iceX: Many languages have eval or similar, and quite a few can do metaprogramming, e.g. Haskell's Template Haskell. The (arguably) unique thing about Lisp is that the representation for data, code, and meta-code is same - not just in syntax, but it really is the same thing.
    – tdammers
    Sep 3, 2013 at 13:56
  • 2
    @iceX Eval is just one part of it, but not all of it. In Lisp you can execute code at compile time. You can execute code at read time. Yes clojure supports all of Lisp's dynamism, it has macros, reader macros and eval, as well as the ability to attach a REPL to a running program to modify it on the fly.
    – stonemetal
    Sep 3, 2013 at 14:50
14

The question is a difficult one to answer, as someone would have to know all languages in order to know that no other has a particular feature available in Lisp, so the following is based on the languages I have experience with.

Off the top of my head, conditions are something that I haven't seen in any other language. Think 'exceptions', but where the call stack is not unwound, and where the caller can send a recovery value to the exception site , but without disturbing the call stack in between the handler and the source of the exception. To be fair, this is really just a special application of continuations, so Ruby and Scheme (at least) can do this.

Lisp's macro system benefits from regularity/homoiconicity, but Scala is planning to incorporate them as a stable feature in 2.12 and Template Haskell claims similar features. I'd argue that they'll be more syntactically complex than with Lisp, but the compile-time generation of code is there regardless.

Come to think of it, though, straight building of forms is only one kind of macro available in Lisp: I haven't seen an equivalent of compiler or reader macros anywhere else.

The ability of some dialects (e.g. SBCL) to save a complete, resumable process image is cool, but again it isn't unique: Smalltalk has been doing that for decades.

Many other languages allow destructuring assignment on when returning arrays, but the #'values and #'multiple-value-bind/let-values approach still seems to be specific to Common Lisp and Scheme (which can still do 'regular' destructuring as well). Perl's 'wantarray' allows a function to determine whether it's being called in a scalar, list or void context so it can adjust its return value in a similar(-ish) way, but I've not seen 'true' multiple return values outside of Scheme/CL.

In terms of language features, there's probably not much that Lisp can do that other languages can't (Turing completeness being what it is). What it is, however, is a language where the code is expressed in terms of its own data structures, making the Big Idea™—that code is data—something that's relatively easy to work with.

1
  • 3
    Scala macros are much less powerful than Lisp macros, as well as TH macros, Scheme hygienic macros, etc. An equally powerful system can be found in MetaLua and Converge.
    – SK-logic
    Sep 3, 2013 at 13:55
4

After so many decades, I don't think there is anything exclusive to Lisp. But even today, there are many interesting things that are hard to find outside Lisps. A few things that come to mind:

  • A high-quality object system with sophisticated meta-protocols (i.e. CLOS) is not remotely popular.
  • multi-methods pop up from time to time somewhere, but most people never heard about them.
  • As others have pointed out, the condition system is quite sophisticated comparing to popular exception handling mechanisms.
  • A compact representation of the language's semantics (eval), an empowering approach to define one's language and making it available for straightforward deep adaptations (see SICP) -- "eval" functions of current popular languages simply don't share the same properties.

Finally, there is a lot more to learn from Lisp that is not about the language itself but became part of Lisp history and got lost in time. E.g. Interlisp, Symbolics Genera, etc...if you never put your hands on Genera, see this comp.lang.lisp thread where Kent Pitman describes how "Emacs is only a pale shadow of Genera's Zmacs" -- which was all enabled by having a powerful Lisp system which Zmacs was part of, which ran on a Lisp Machine.

3
  • I've never seen a good explanation of multimethods that distinguished them from overloaded methods, a standard feature in nearly every modern imperative language, except by the fact that multimethod dispatch is resolved dynamically at runtime and is therefore much slower than using overloaded methods, which are resolved at compile time. Feb 1, 2014 at 21:38
  • They have important distinctions. If you are in trouble finding resource on that, you can always create a new question here.. Feb 1, 2014 at 23:23
  • Julia has multimethods, and Haskell's parametric polymorphism is sort-of like multimethods. There are ways of simulating multimethods in many languages. The condition system is something I really want especially (edit and continue, though I've seen it for C# debuggers).
    – aoeu256
    Sep 14, 2019 at 23:09
4

It's not necessarily a certain single feature. It's the whole look and feel, and how certain sets of features work together.

JavaScript or Java have a lot of features of Lisp (virtual machine, compiler/evaluator, garbage collection, etc.). But JavaScript for example lacks the symbolic programming part, it lacks mathematic capabilities (internally it has only floats), it lacks the error handling, and so on.

Many Common Lisp systems are optimized for a development way where one extends new software incrementally, by extending the Lisp language in various dimensions using various meta-programming techniques - without restarting the software for a long time. Thus it needs to be flexible and extensible - but at the same time it needs to be robust. Changing the language (macros are basically a way for the user to extend the compiler) without crashing the program.

Now something like JavaScript is also used to extend a program, typically a web browser. But most of the time one does not much meta-programming in JavaScript - besides some OOP hackery.

Example:

One can implement a general advanced mathematics software for the domain of computer algebra in mostly two ways: write the engine in C with a specialized language on top (like Mathematica) or in some more advanced Lisp dialect. Macsyma/Maxima in Common Lisp, Reduce in Standard Lisp, Axiom in Common Lisp.

(There are also one or more written in Python.)

There are not many systems which offer the feature set of something like Axiom, which runs on top of Common Lisp.

What made Lisp attractive for these types of applications is a mix of features: advanced basic maths (bignums, ratios, ...), symbolic computation, interactive compiler, etc. It's quite possible to get these things by implementing them in a low-level language. That way, one will have implemented 50% or more of a typical Lisp system.

0
2

Not as far as I am aware of. Forth is easily as dynamic as Lisp, perhaps more so since dynamic code in Forth looks like regular Forth code while Lisp macros tend to use different features than regular Lisp code (in Clojure at least I have never used syntax quote outside of a macro) and as a result they look really different from regular Lisp code. As an example of how dynamic Forth is, here is a way to implement comments in Forth:

: (   41 word drop ; immediate
( That was the definition for the comment word. )
( Now we can add comments to what we are doing! )
6
  • 1
    As fun as Forth seems to be, I always found it hopelessly opaque and awkward, perhaps because it is stack-based and everything is reversed. Sep 3, 2013 at 15:54
  • 2
    Out of curiosity, what do you mean by 'segregated from the main part of the language'? Macros in Lisp have full access to all functions defined at the point that the macro is defined, any of which can be used to build the form that the macro expands to. This could involve information retrieved from a database and/or server. Your comment example, could be defined as: (defmacro comment(&rest body)), couldn't it? Sep 3, 2013 at 15:58
  • 1
    @DannyWoods I mean macros don't look like regular code. At least in clojure you can tell macro code from regular code because it heavily uses syntax quote, unquote splicing, etc which is unusual in regular code. That example forth code I gave looks like normal code right up till you see the immediate. Rereading my answer it is just poorly expressed.
    – stonemetal
    Sep 3, 2013 at 20:42
  • 1
    @stonemetal No worries, but it's worth noting that there's nothing macro-specific about syntax quote, in either Common Lisp or Clojure. It's sometimes convenient to have backtick expressions in regular function definitions, and macro bodies can be built manually with plain lists (although you only have to do this once or twice to decide that syntax quote is a good thing!) Sep 3, 2013 at 21:34
  • 1
    To be fair, the same can be done in Lisp, by registering a custom reader macro.
    – fjarri
    Sep 4, 2013 at 1:18
2

Lisp has many dialects, and each of them has its own set of features. My favorite feature that is unlikely to be adopted by any other language is the "spaghetti stack" from Interlisp.

The spaghetti stack is like a closure, but on steroids. It saves not only the current function, but the entire context up to the top of the stack. Something like a co-routine, except that you could create these arbitrarily, resulting in a hierarchy of stack contexts.

3
  • Didn't know that, gonna check it out, thanks for your answer :)
    – iceX
    Sep 3, 2013 at 7:23
  • 7
    Is this the same as Scheme's continuations? Sep 3, 2013 at 10:43
  • 1
    @NicolaMusatti, a "spaghetti stack" is a common implementation strategy for Scheme-like continuations (which can be called after the function which created them has returned).
    – Alex D
    Feb 1, 2014 at 13:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.