Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

There are some other explanations given in sibling comments. They may be right, but there's another point that may also explain some of this sentiment.

In Common Lisp and its antecedents, a "list" is a chain of cons cells, as mentioned by some of the sibling comments, but that's not all. Another important point is that in Common Lisp and its antecedents, source code is not made of text strings; it's made of Lisp data structures--atoms and chains of cons cells.

A text file containing "Common Lisp source code" does not actually contain Common Lisp source code. It contains a text serialization of Common Lisp source code (one of many possible text serializations, actually). It isn't actually Common Lisp source code until the reader gets done with it.

This might sound like a trivial pedantic point, but it isn't. Because Common Lisp source code is made up of standard Common Lisp data types, its standard library provides everything you need to walk arbitrary source code, deconstruct it, transform it, construct it, and compile it. Those features are all built into the language in a way that they are not in most languages.

For those of us who are accustomed to using those features regularly, working with a language that lacks them is such an impoverished experience that I can understand folks objecting that "that's not a proper Lisp." I don't tend to make that objection myself, but I do understand it.

If your Lisp does not represent its source code in this way, or if it doesn't even have the data structures that source code is made of in Common Lisp and its antecedents, then there is a nontrivial sense in which it's not a Lisp--or at least not the kind of Lisp that those older ones are.



sort by: page size:

Lisp represents source code as lists. It’s a huge myth that modern Lisp just uses lists as the only data structure.

LISP code is not a list as you know it traditionally. It's made up of cons cells. The code of other languages is not a cons cell, altho it is a list of characters. Very important distinction.

Common Lisp uses cons cells. So do a bunch of older Lisps whose design fed into the design of Common Lisp. That's all. There's nothing else special about cons cells in my mind.

Cons cells are not the important point--at least not for me. The important point in this context is that expressions are represented by something better (that is, more conveniently-structured) than flat text strings, and that the representation be a standard data structure in the language, and that operations on source code are implemented by APIs that are exposed as standard parts of the language.

As far as I'm concerned, if a language does that, then it's nailed this particular part of being Lispy. There are some other parts, but that discussion is outside the scope of this one.


> textual representation is irrelevant to the semantics of the program

It's not completely irrelevant, since Common Lisp has the idea of a text-based file with source code and a compilation mode for it (-> COMPILE-FILE).

But generally what you say is true. We see that especially in two places: 1) the Lisp interpreter, which runs non-text s-expressions and 2) special development environments, like Interlisp-D/Medley where a prominent way to edit Lisp code is a structure editor, which manipulates s-expressions in memory.


Lisp code is made of lists. And lists are the main data type for lisp programs. So you can naturally produce and transform lisp code using lisp code. Not all lisps allow that, though, but I think that's the main differentiator and it's unique in that aspect.

So it's like generating JS code text from JS, but working with JS code as a text is much more confusing compared to working with lisp code as a list.


More from Wikipedia (which this version doesn't have):

The name LISP derives from "LISt Processing". Linked lists are one of Lisp languages' major data structures, and Lisp source code is itself made up of lists. As a result, Lisp programs can manipulate source code as a data structure, giving rise to the macro systems that allow programmers to create new syntax or even new domain-specific programming languages embedded in Lisp.

It also lists the following under "Syntax and semantics":

    Symbolic expressions           — Check
    Lists                          — Check
    Operators                      — Check
    Lambda expressions             — Check
    Atoms                          — Check
    Conses and lists               — Nope (not linked lists)
    S-expressions represent lists  — Nope
    List-processing procedures     — Check
    Shared structure               — Nope (not linked lists)
    Self-evaluating forms and quoting — Nope
    Scope and closure              — Kinda
    List structure of program code — Nope
    Evaluation and the Read-Eval-Print Loop — Nope
    Control structures             — Nope
The biggest difference which doesn't really make this a Lisp is probably that the code isn't a list, it's just a series of yield expressions. You can't really parse it as a list or manipulate it as a list. It's also missing cons/car/cdr which is sort-of the building block of Lisp languages!

> Lists are not very important for Lisp, apart from writing macros.

I'm not sure I agree. Sure, in most dialects you are given access to Arrays, Classes, and other types that are well used. And you can choose to avoid lists, just like you can avoid using dictionaries in Python, and Lua. But I find that the cons cell is used rather commonly in standard Lisp code.


Now I have a full keyboard instead of that clumsy phone.)

The very idea "to implement a List" in what is supposed to be called a Lisp is.. well, I do respect the amount of work that has been done, but.

Such kind of confusion, it seems, comes from a wrong perspective of what a Lisp is. Let's look first at what it is not.

It is not some "modern OO" language with parenthesized prefix notation, it could be so boring. Second, a List is not some "data structure in Lisp", it is the most important, non-separable fundamental feature of the language. Without "native conses" a Lisp is nonsense.

The explanations is going to grow lengthy, but it worth a try.

A Lisp is a set of layered DSLs and ADTs. Its "miracle" in what it is small and good-enough, in accordance with "Less is more" maxima.

There is no fucking "Class Node" in what a List is. It is just (cons 1 (cons 2 (cons 3 '()))) or 1:2:3:[] if you wish.

The big ideas is to separate clearly the use from the implementation, which is called an Abstraction Barrier.

A complete definition of the basic building block - a cons cell, or the Pair ADT is cons, car, cdr, pair? and nil while a List is just one more "constructor" - list. This is minimal and it is good-enough.

It is so "good" that it "created" another small miracle - cadr/cddr notation, which is just an accidental piece of beauty created by a human mind (like a Lisp itself). Only complete tasteless person could abandon this in order to use dull and mediocre first/rest or any other names for selectors that doesn't "chain" and reflect the notion that almost everything could be made out of conses.

The problem is that we have way too many of those who cannot grasp the beauty of what a cadr or cddr is.

Implementation in a language's runtime is quite another topic. There we probably should have a notion of a pool-based allocation (in order to not call malloc for each cons) and locality (to properly implement persistence) etc. but these implementation specific things must never leak int "high-level" use. Again, there is no fucking Class Node.

Speaking of Lisp apart form Lists is like speaking of water apart from the waves. Water has waves, Lisp has Lists in its own nature.

Giving this nature of Lists we could have a general mapper procedure, or map. The idea of a Functor from Math (or these control abstractions from Haskell) has nothing to do with it. It is just the recursive nature of List's structure allows us to define recursive processes.

Even trees not necessarily must have any Class Node. Here is a classic example:

   (define make-tree cons)
   (define datum car)
   (define children cdr)
   (define (leaf? n)
      (null? (children n)))

   (define (tree-map fn tree)
      (make-tree (fn (datum tree))
	     (map (lambda (ts) (tree-map fn ts))
		  (children tree))))
There is no "Single-Linked List Data Structure in Lisp" because there is no Lisp without conses.

Last but not least. This very site is build in a dialect of Lisp which never tries to redefine what a List is.

More about Lisps - http://karma-engineering.com/lab/wiki/Bootstraping

More about piling up useless abstractions - http://karma-engineering.com/lab/wiki/Monads


Except that in LISP, code is not fundamentally textual. It's S-expressions, which are nested lists, ie trees.

These just sound like complaints from a typical novice to LISP. LISP syntax is made of nested lists (an AST) and atoms. Representing sequential types as atoms with ugly reader macros based on the implementation of said sequential types strikes someone who knows a real LISP language as an ugly and ignorant thing to do.

If you find real LISP code unreadable because it doesn't look like javascript - because you can't tell when your sequence is a linked list or an array when there's no curly braces - I suggest learning to read and write LISP code and understand how LISP works. A real LISP I mean, not clojure.

Again, other LISPs have data structures aren't from linked list. A complete myth that that they only use linked lists.


Common Lisp allows one to use arrays or other structures, and can even be used to inline assembly. So despite lists being a major part of the ecosystem and code representation itself, they are not mandatory for implementing an algorithm, or necessarily a performance drawback to using lisp. By the same token, it's easy for people to accidentally use lists and pointers to implement algorithms just as inefficiently in other languages - Python comes to mind. A standard approach in Python is to lean on external libraries for high performance computation, and this can be done just the same way in Lisp - but Lisp can also be used directly to write efficient low level algorithms by making a conscious effort not to use the list- and pointer-based functions to do so.

It's actually Language of Irritating Superfluous Parenthesis.

Anyway, even saying "code is linked list" does not really cut it as you can represent 'ordinary' programming language program as a list of tokens. What makes LISP special is that code is a first class citizen and the mapping between code you edit and the programs run-time memory representation is trivial. Combined, you get this special feeling that "code is data".


I agree with all your points.

One of my main gripes with Common Lisp is its complexity. When I started learning Lisp, I expected to find a beautiful and elegant language, one without a need for syntax. The way C is almost an elegant assembly. Maybe I should have tried Scheme instead.

Common Lisp is anything but elegant. Instead of a regular syntax, you get all these "mini-languages", like the format syntax e.g.

    (format t "~{~a~^, ~}" '(1 2 3))
    ; prints out
    ; 1, 2, 3
This works fine for list, but not for vectors, which is a problem, since I needed vectors often (for random access).

The loop macro is also quite powerful and complicated. e.g.

    (loop :for i :from 1 :to 1000 :collect (* i i))
will create a list of square of 1 to 1000. The loop macro can become really convoluted, not at all simple.

The fact that Common Lisp is a Lisp-2 makes treating functions as first-class as rather awkward. I could never remember when I needed to prepend a lambda with #'.

This article may be of interest http://xahlee.info/comp/Common_Lisp_quotations.html

I found much of the elegance I was expecting in Haskell. But maybe Scheme is a better lisp.


Previous lisps had only the list as a datastructure with a special syntax and the program was only represented as lists.

There are different "schools" now. Some argue that representing the program only with lists is more uniform and simple. Rich Hickey argues that the different multiple usages of lists in "traditional" lisps, such as in Common Lisp, imposes a cognitive burden so increase the complexity.

The Janet language for example follows Clojure whereas as Racket is still a modern language but follows the "tradition".


Because it's a lisp, code is a data structure

I think what is meant is that the list is the primary data structure for everything in lisps. Fortunately, it is not the only data structure in lisps. I dislike constructs where eg. a graph edge is built as:

'(GRAPH-EDGE <source-vertex> <target-vertex>)

whereas in ML you would write

type edge = { source: Vertex.t; target: Vertex.t }

which is not a list, but an entirely different representation. In Common Lisp the representation could of course be a CLOS object or a DEFSTRUCT (I think, my CL-fu is definitely not that good).

As for the composition, lisps are lacking (nice) currying which is one of the basic building blocks when constructing combinator-libraries.


I think the point to take home here is that LISP has a rich set of functions and building blocks around lists. Indeed, if you have something you want to do to a list, that function probably already exists.

Contrasted with many other languages that are catching up, but sometimes have surprising amount of boilerplate to process data.


> Lisp has built in classes

Objects aren't compound values. Objects are compound, well, objects. And pointers to objects are primitive, indivisible values - not compound!

> lists

Racket and ML have lists. Lisp has mutable linked data structures, built out of `cons` cells, whose value at any given point in time may be a list. But the value itself isn't first-class, because you can only bind the identity of the cons cell to a variable.


>since all data lives in lists in lisp

That's not true. Every modern Lisp implementation has many primitive data structures: vectors, hash tables, etc.

next

Legal | privacy