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".

Peter Mortensen
  • 1,050
  • 2
  • 12
  • 14
iceX
  • 691
  • 5
  • 8
  • 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 [ask] – gnat Sep 03 '13 at 06:14
  • 3
    @gnat Thx for advice, and I've updated my question :) – iceX Sep 03 '13 at 07: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. –  Sep 03 '13 at 08:51
  • 9
    I guess there is no other language where round brackets are the only form of grouping for everything :-) – Doc Brown Sep 03 '13 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. – daniel gratzer Sep 03 '13 at 16:47
  • The language Algol helped to shape the "function as a first-class citizen". MacCarthy's Lisp didn't have lexical scope; the lambdas were just code, not capturing any environment. – Kaz Oct 21 '16 at 04:54
  • However, I don't think Pascal and especially the C derived languages had "functions as first-class citizens". I believe Julia's metaprogarmming can be almost as good as Lisp. You can also use Hy to do macros in Python just like Lisp. – aoeu256 Sep 14 '19 at 22:55

6 Answers6

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.

Greg Hewgill
  • 10,181
  • 1
  • 46
  • 45
  • 2
    Those features aren't unique to Lisp (and directly-derived variants), and haven't been for quite a long time. – Donal Fellows Sep 03 '13 at 05:33
  • I think I don't understand the point 9. Is it saying that it can change the code in runtime? thought that some dynamic programming language like javascript can do the same... could you please explain it to me? – iceX Sep 03 '13 at 07:31
  • @iceX you are right, any language with `eval` can compile and run at runtime – ratchet freak Sep 03 '13 at 07:56
  • @iceX: I wouldn't say that you can just change any code at runtime. However, you can certainly create, compile, and introduce *new* code at runtime in Lisp. Also (through macros) you can run code that changes how code is compiled. – Greg Hewgill Sep 03 '13 at 08:25
  • 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.) – Jörg W Mittag Sep 03 '13 at 08: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 03 '13 at 09: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 03 '13 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 03 '13 at 14:50
  • 1
    [Rebol](http://en.wikipedia.org/wiki/Rebol) was around in 2002 when that was written and had both of those features back then. My favorite example was the 1-line editable GUI quine – Izkata Sep 04 '13 at 16:51
  • @lzkata do you have a link? In java or clojure there is also a small hello world like this: (javax.swing.JOptionPane/showMessageDialog nil "Hello World") – AndreasScheinert Sep 05 '13 at 09:07
  • @AndreasScheinert (It's an "i" not an "L", else I'd've seen your message earlier - had no notification) While what I said about the structure is true (the `block!` datatype is used for both code and data, so self-modification is possible), the quine I'm remembering is cheating a bit by using `do`, the language's equivalent of eval/exec - [number 7 on this list](http://www.rebol.com/oneliners.html) – Izkata Sep 06 '13 at 03:15
  • JavaScript REPLs seem pretty good, and are getting better. Python has IPython which can be connected to Pycharm and those notebooks, if you use reload or deepreload you may have to reseat all of your classes (recursively go through sys.modules.__dict__['obj'].__dict__['subobj'].__class__ = sys.modules.__dict__['obj'].__dict__['subobj']). It should be possible to save your image by pickling your sys.modules, however, Jupyter notebooks already figured that out. – aoeu256 Sep 14 '19 at 23:04
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.

Mark Hurd
  • 343
  • 1
  • 3
  • 12
Danny Woods
  • 841
  • 1
  • 6
  • 9
  • 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 03 '13 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.

Thiago Silva
  • 1,039
  • 8
  • 9
  • 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. – Mason Wheeler Feb 01 '14 at 21:38
  • They have important distinctions. If you are in trouble finding resource on that, you can always create a new question here.. – Thiago Silva Feb 01 '14 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 '19 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.

Peter Mortensen
  • 1,050
  • 2
  • 12
  • 14
Rainer Joswig
  • 2,190
  • 11
  • 17
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.

Peter Mortensen
  • 1,050
  • 2
  • 12
  • 14
ddyer
  • 4,060
  • 15
  • 18
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! )
Peter Mortensen
  • 1,050
  • 2
  • 12
  • 14
stonemetal
  • 3,371
  • 16
  • 17
  • 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. – Robert Harvey Sep 03 '13 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? – Danny Woods Sep 03 '13 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 03 '13 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!) – Danny Woods Sep 03 '13 at 21:34
  • 1
    To be fair, the same can be done in Lisp, by registering a custom reader macro. – fjarri Sep 04 '13 at 01:18
  • Hmm Forth could be made usable with a good IDE that color-codes stuff (more than Color Forth) and shows you test-cases flows. – aoeu256 Sep 14 '19 at 23:57