Hacker News new | past | comments | ask | show | jobs | submit login

> Scheme is even more Lisp than Lisp

That's not true. In fact, there are some prominent people in the Lisp community who do not think that Scheme is a Lisp.

See this thread from 2002 on comp.lang.lisp (especially Pitman and Naggum's comments) for more. If you are too young to have posted on C.L.L when Usenet was still popular, and want to see what a full fledged flame war looks like, look at the end of the thread.

Some people who posted there (including the OP) are on HN.

https://groups.google.com/forum/#!topic/comp.lang.lisp/Bj8Hx...




There are people — like Pitman and Naggum — who argue for defining "Lisp" to mean "Common Lisp" for political rather than technical reasons, essentially because they want to stop the evolution of Lisp. (Those who haven't read the thread might think I'm exaggerating.) Fortunately, they are relatively irrelevant today, and Lisps like Clojure, Hy, and Racket are carrying on the further development of Lisp.


But languages like Clojure, Hy or Racket are not a Lisp. They are Lisp-derived/inspired/... languages. That's also why they don't have Lisp in their name.

OTOH Lisps like Emacs Lisp, Common Lisp, ISLisp, and some others are still carrying the core of the first Lisp. Basically you can run Lisp 1.5 code in those without many changes.

Just like Java is not the new C. OTOH languages like C++ and Objective C still support much of C. You can port C to C++ easily.

Over the history many other languages have been trying to replace Lisp: ML (functional programming), Dylan (object-oriented programming), Logo, Scheme, ... all tried to modernize/improve Lisp. Now it is Clojure and some other languages.

Still there are core Lisp languages which carry on the genes.

But not Clojure:

    Clojure 1.5.0-RC2
    user=> (car '(1 2))
    CompilerException java.lang.RuntimeException: Unable to resolve symbol: car in \
    this context, compiling:(NO_SOURCE_PATH:3:1)
What?

    user=> (append '(1 2) '(3 4))
    CompilerException java.lang.RuntimeException: Unable to resolve symbol: append \
    in this context, compiling:(NO_SOURCE_PATH:4:1)

What?

    user=> (assoc 'foo '((foo . 3)))
    ArityException Wrong number of args (2) passed to: core$assoc  clojure.lang.AFn\
    .throwArity (AFn.java:437)

    user=> (atom 'foo)
    #<Atom@2285af04: foo>
???

Does not look like it supports Lisp processing as defined by McCarthy:

http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.91....

Additionally the Java error messages are especially irritating. I enter Clojure code and get Java error messages.

Compare that with a real Lisp:

    bash-3.2$ java -jar abcl.jar
    Armed Bear Common Lisp 1.1.1
    Java 1.7.0_25 Oracle Corporation
    Java HotSpot(TM) 64-Bit Server VM
    Low-level initialization completed in 0.628 seconds.
    Startup completed in 1.968 seconds.
    Type ":help" for a list of available commands.
    CL-USER(1): (car '(1 2))
    1
    CL-USER(2): (append '(1 2) '(3 4))
    (1 2 3 4)
    CL-USER(3): (assoc 'foo '((foo . 3)))
    (FOO . 3)
    CL-USER(4): (bar 10)
    #<THREAD "interpreter" {6EEE1E2}>: Debugger invoked on condition of type UNDEFINED-FUNCTI\
    ON
      The function BAR is undefined.
    Restarts:
      0: CONTINUE     Try again.
      1: USE-VALUE    Specify a function to call instead.
      2: RETURN-VALUE Return one or more values from the call to BAR.
      3: TOP-LEVEL    Return to top level.
    [1] CL-USER(5): 
We even get Lisp error messages. The compiler is written in Lisp. http://svn.common-lisp.net/armedbear/trunk/abcl/src/org/arme...

Clojure may be a cool language, but the core of Lisp has been replaced in parts with something else. List processing is different. It's now based on 'persistent' lazy sequences...


Rainer, are you seriously complaining that Clojure is not a Lisp because it spells "car" as "first", "cdr" as "rest", and "append" as "concat"? That makes as much sense as complaining that Interlisp is not a Lisp because it doesn't have FEXPR.

In Clojure, your examples are spelled:

    (first '(1 2))
    (concat '(1 2) '(3 4))
    (get {:foo 3} :foo)       ; or (get {'foo 3} 'foo)
    (symbol? 'foo)            ; or (not (coll? 'foo))
And lots of things in the world irritate me for reasons that have nothing to do with whether they are Lisp or not.

I'd like to point out that your examples wouldn't have worked in Lisp 1.5 either. You would have had to type, for example,

    CAR (QUOTE (1 2))
interactively.


Clojure does not implement Lisp lists. Just plain and simple. It may partly look like Lisp lists. But they aren't. Functions are randomly either looking compatible or not at all. ATOM does something completely different. CAR does not exist. FIRST exists, but works on persistent lazy sequences (wonderful, but it is not using Lisp-style chained cons cells).

Btw., LispWorks, plain:

    CL-USER 23 > CAR (QUOTE (1 2))
    1
A more complex example:

http://www.informatimago.com/develop/lisp/com/informatimago/...


You're mostly mistaken, and you've mostly been fooled by changing names.

Clojure lists aren't lazy, and they support the Lisp operations, under slightly different names, and with the same asymptotic performance characteristics you're used to, because they're made of chained cons cells. The cons operation is implemented at https://github.com/clojure/clojure/blob/master/src/jvm/cloju.... Your "more complex example" is a 1960 program written, originally, in M-expressions; here's the first definition on the cited p.32:

    th1r[v;a1;a2;c1;c2] = [atom[v] → member[v;a1]∨
        th[a1;a2;cons[v;c1];c2];T → member[v;a2]∨
        th[a1;a2;c1;cons[v;c2]]]
As far as I know, there hasn't ever been a Lisp system that could parse and run that, and certainly not LispWorks. But you can straightforwardly transliterate it either into Lisp 1.5, or Common Lisp (although the post you link doesn't bother; instead they wrote an interpreter for the incompatible Lisp 1.5 syntax in Common Lisp), or into Clojure:

    (defn th1r [v a1 a2 c1 c2]
        (cond (symbol? v) (or (some #{v} a1) (th a1 a2 (cons v c1) c2))
              true        (or (some #{v} a2} (th a1 a2 c1 (cons v c2)))))
The only weird thing here is using the set-membership test instead of MEMBER.

Compare the above to the LISP 1.5 version:

    (TH1R (LAMBDA (V A1 A2 C1 C2) (COND
        ((ATOM V) (OR (MEMBER V A1)
        (TH A1 A2 (CONS V C1) C2) ))
        (T (OR (MEMBER V A2) (TH A1 A2 C1 (CONS V C2))))
        )))
Clearly Lisp changed a lot between LISP 1.5 in 1962 and CLtL in 1984, not to mention current Common Lisp practice; what you'd write in any modern Lisp looks a lot more like the Clojure version than the LISP 1.5 version.

The set thing, though, points to a real difference in Clojure: it has a set data type, and you're expected to use it instead of the list data type when what you want is a set. It's still immutable, though, and supports efficient nondestructive update, so if you decide to rewrite this as

    (defn th1r [v a1 a2 c1 c2]
        (if (symbol? v)
            (or (a1 v) (th a1 a2 (conj c1 v) c2))
            (or (a2 v) (th a1 a2 c1 (conj c2 v)))))
you can be assured that you're not totally hosing your program's performance. It might get better, in fact, if the sets are large.

You could argue that adding sets (and finite maps) to Lisp is violating the underlying essence of Lisp, in which you use conses for everything, but in fact we already have vectors, hash tables, classes, structs, and closures in all of the other Lisps you're citing, none of which were present in the Ur-Lisp in 1960.

ATOM doesn't exist in Clojure, although you can define it. atom does, and yes, it does something totally different from what ATOM did historically.

By "persistent" Clojure means "immutable". That is, there's no RPLACA or RPLACD. But RPLACA and RPLACD weren't present in McCarthy's original proposal, and they're hardly central to Lispiness. You could even argue that their absence is Lispier, since it encourages functional programming and enables the compiler to optimize it better, and indeed Racket already abandoned them a few years back for that reason.

BTW, I was wrong about what you would have had to type at LISP 1.5's read-apply-print-loop. You would have had to type

    CAR ((QUOTE (1 2)))
for obvious reasons.


I'm not mistaken. Look closer.

Of course Clojure sequences are lazy.

    (defn map
      ([f coll]
       (lazy-seq
        (when-let [s (seq coll)]
          (cons (f (first s)) (map f (rest s))))))
Inside a LAZY-SEQ the operations are just that.

They are also not acting like cons cells:

    user=> (cons 1 2)
    IllegalArgumentException Don't know how to create ISeq from: java.lang.Long   clojure.lang.RT.seqFrom (RT.java:496)
> As far as I know, there hasn't ever been a Lisp system that could parse and run that

What you saw in the file was the source code used by the first Lisp implementation.

> an interpreter

That was no 'interpreter'. Mostly a different reader.

> what you'd write in any modern Lisp looks a lot more like the Clojure version than the LISP 1.5 version.

Not necessarily. See the Lisp source for http://www.shenlanguage.org/Download/download.html

Plain old-style Lisp code.

There are not many modern Lisp books. If you look at PAIP, that is old-style. PCL is more modern. But neither has the look or feel of Clojure.

But I'm not saying that Lisp's don't contain new stuff. I'm saying that they contain the old stuff + new stuff.

Clojure gets rid of core old stuff:

* names are different

* concepts are removed (mutable cons cells, ...)

* syntax is different from every other Lisp

* semantics is different (persistent data structures, ...)

> By "persistent" Clojure means "immutable"

immutable and persistent are different, but related concepts. A data structure can be immutable, but it does not need to be persistent. 'Persistent' means that an immutable data structure can be updated, keeps all its versions and provides certain performance/space characteristics (one does not do full copies for example).

Imagine a list (f o o b a r). Now I want to insert 1 between f o o and b a r.

Immutable: I need to make a new list with elements from the old list copied.

Immutable and Persistent: I make a new list, but I possibly reference parts of the old data structure. Thus I don't need to make a full copy.

Lisp's lists/vectors/... are not immutable and also not persistent.

Clojure replaces that. Just read http://clojure.org/sequences


Some Clojure sequences are lazy, but Clojure lists aren't. And you are right that there's a typing constraint that prevents you from making improper lists, and that's not the traditional behavior of Lisp conses.

As for the 1960 code in the manual, no, that wasn't the source code used by the first Lisp implementation.

In the Shen case, I assume you're talking about things like this, in primitives.lsp?

    (DEFUN eval-kl (X) 
      (LET ((E (EVAL (shen.kl-to-lisp NIL X))))
          (IF (AND (CONSP X) (EQ (CAR X) 'defun))
              (COMPILE E) 
              E)))
What I see here:

    - Indentation to show structure;
    - DEFUN;
    - ';
    - IF;
    - LET;
    - some lowercase letters.
To me, that looks a lot like the modern Clojure code, and not much like the 1962 code.

Your taxonomy of immutability and persistence is interesting; thank you. I thought you might have meant that Clojure lists were automatically serialized to stable storage on, for example, program exit. Lisp lists have always been persistent, then, except when you mutate them? Because you can make your (f o o 1 b a r) from (f o o b a r) without copying the whole thing in any Lisp.

Lots of Lisps have been backwards-incompatible with previous Lisps. Scheme, Common Lisp, Emacs Lisp, and even MACLISP and LISP 1.5 were all significantly backwards-incompatible with their predecessors. That didn't make them non-Lisp. Common Lisp was not the end of Lisp development.


> Lots of Lisps have been backwards-incompatible with previous Lisps. Scheme, Common Lisp, Emacs Lisp, and even MACLISP and LISP 1.5 were all significantly backwards-incompatible with their predecessors.

Right. That's what I'm saying. Clojure does not care to be backwards compatible with Lisp.

> Your taxonomy of immutability and persistence is interesting

That's not mine.

Clojure took its base data structures from Haskell and modern ML.

Not Lisp.

See:

http://www.cs.cmu.edu/~rwh/theses/okasaki.pdf

http://en.wikipedia.org/wiki/Persistent_data_structure

The book comes with examples in ML and Haskell.

http://www.amazon.com/Purely-Functional-Structures-Chris-Oka...


I don't think we're going to get anywhere further in this conversation, although I really appreciate everything you've posted so far, and I heartily second your recommendation of Okasaki's book, even though I haven't finished it myself. And I hope that I have avoided being anything like Erik Naggum in this conversation, despite my frustrations.


This conversation was incredible!


> But languages like Clojure, Hy or Racket are not a Lisp.

How are they not. Okay, I see your argument with Clojure, but how does that apply to Racket?

  Welcome to Racket v5.3.4.
  > (car '(1 2))
  1
  > (append '(1 2) '(3 4))
  '(1 2 3 4)
  > (assoc 'foo '((foo . 3)))
  '(foo . 3)
  >


Racket has some other differences: Immutable data structures, more emphasis on Functional Programming and less on imperative programming, symbols are not that important, slightly more static and less interactive. It's basically developed from R6RS on. The Scheme community is divided about that. The R7RS under development goes a bit more back to the older spirit and style - which is also controversial.


Sure, Racket has differences from CL, but I don't see how havi any of the things you mention make it "not a Lisp" . Clearly, it's not CL, but you don't seem to be making the "CL is the only Lisp" argument.


I got to the end of the page and thought, "Well that wasn't so bad..." -- then I saw that I was at the end of page 1...of 22.

Epic.


Civil wars over distinctions without a difference are the ugliest kind.


Holy smoke...

I started posting on Usenet ~1989. I've seen a number of flame fests that I considered epic but this moved the bar exponentially.


And he started it with this.

I hesitate to ask this question because I really don't want to start a flame war.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: