Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Well, the laziness yes, thanks for the correction, but much of it was from Clojure's own page about how it is different from other Lisps. And I am aware of Hickey's history. I was trying to provide a summary of the difference in outlook between Clojure and Kawa/ABCL: Clojure is decisively more "java-ey" than either of the others, and very much feels like a lisp for those already familiar with that language, with some FP on the side. I don't know if that's just my interpretation of Hickey's ideas, or if it was a deliberate design decision, or if it fell out of the Java interop. It could be any of them. But the language has a distinctly unlispy feel as a result, even though it IS a lisp, lack of cons cells not withstanding.


I always read the history as Hickey wanting first-class Java interop after his experience with FOIL[1]. In addition he wanted Clojure to be specifically for functional programming (something Common Lisp doesn't excel at).

IMO, most of the things that make early versions of Clojure different from previous Lisps do fall out from the requirement of first-class Java interop and being opinionated about pure data being the Right Way to do multithreaded.

From that point on Clojure is its own thing, and will naturally diverge.

1: http://foil.sourceforge.net/


I think we have to get rid of the idea that any language with parentheses is a Lisp. For all practical purposes languages like Scheme, Lisp, Clojure are fully incompatible on all levels: syntax, semantics and pragmatics. All they share are some distant ideas (some form of s-expression syntax, some form of macros, ...) provided in incompatible ways. They share no code, no libraries, no books, no community, ... that's a consequence of being different.

Note that this says nothing about their usefulness for developing software.


...And I think we need to get rid of the idea that those differences necessarily precludes them from being Lisps. Because make no mistake, they are all Lisps.

You said that they didn't share code, libraries, books, or community. First off, they do share community to some degree: there's a good bit of overlap between all of them. It's not all overlap, but there's plenty there.

Second off, you know what other languages don't share code, library, or books with the Common Lisp we know today? MACLisp. Interlisp. Portable Standard Lisp. Lisp 1.5. Emacs Lisp. AutoLisp. ISLISP. Eulisp. Arc. To say nothing of Hy, LFE, MDL, Newlisp, NIL, Picolisp, and yes, Clojure and Scheme. And there are countless others.

Lisp has always been a language of many different dialects. Why should we pretend it's any different now?


Second tthis. The term Lisp in CS is like Romance in linguistics, it indicates a family of languages that belong to different similar but not identical cultures which contribute to them and yet still the languages come from a common ancestor and are more similar than not while at times not easily mutually comprehensible.


People who can speak German dialects can understand each other. Books written in some form of German can be understood by most readers.

Same for English dialects.

For Germanic languages this is not the case. English and German are both Germanic, but knowing German does not let me read English literature.

Lisp is like English: a basic vocabulary, a basic syntax, basic semantics understood by all readers. I can look at some basic Lisp code and I will understand the program. A Lisp developer looking at Clojure code will understand very very little.


English is such an oddball in the Germanic family that I'd count it an outlier. About 40% of its vocabulary is of Latin origin, and in sum about 60% of its vocabulary is made up of loan words. A couple years ago I submitted the "Uncleftish Behaviour" here [1], which demonstrates English with only germanic words.

[1] https://news.ycombinator.com/item?id=7671549

For the rest of the family, there's a lot of mutual intelligibility. And, a metaphor is a metaphor, no metaphor holds if you dig deep enough.

> A Lisp developer looking at Clojure code will understand very very little.

This is an assumption, not a fact. Java interop code and (recur) are a bit confusing in the first sight, but not undecipherable.

The prog lang that's most like English is C++: too many things are overtly overloaded, see phrasal verbs.


Clojure borrows from C++, C#, Common Lisp, Erlang, Haskell, Mathematica, ML, Prolog, Scheme, Java, Racket, Ruby - according to Wikipedia.

I would guess that 10% of its operator names come from Lisp. Okay, that's a wild guess. Let's look at the special forms:

def-, if+, do-, let+, quote+, var-, fn-, loop-, recur-, throw-, try-, monitor-enter-, monitor-exit, ...

Around three from thirteen special forms have roughly the same name/meaning in Lisp.

Let's look at printing: pr-, prn-, print-, println-, newline-. None Lisp names, either they don't exist in Lisp under that name or they do something else. PR and PRN don't exist in Lisp. PRINT prints a newline before printing the object. PRINTLN does not exist in Lisp. NEWLINE is called TERPRI. Thus even for a simple thing as basic IO the API is different.


Small differences in IO are usually simple to deal with, and quite common between other lisps, such as Scheme, and picolisp.


Really? It's pretty easy to get, with a few pointers here and there.


Good luck reading Thomas Mann or Hermann Hesse in German...

Any modern fiction. No chance.


rolls eyes

Not German, Clojure. Clojure is pretty easy.


Really? You must be really good at Clojure. Then you can explain this:

    user=> (list 1 2 3)
    (1 2 3)
    user=> (list? (list 1 2 3))
    true
so (1 2 3) is a list.

    user=> (cons 0 (list 1 2 3))
    (0 1 2 3)
    user=> (list? (cons 0 (list 1 2 3)))
    false
(0 1 2 3) is not a list?

    user=> (list? '(0 1 2 3))        
    true
But then it is???

WTF?

Compare ELISP:

    ELISP> (listp (list 1 2 3))
    t
    ELISP> (listp (cons 0 (list 1 2 3)))
    t
and CL:

    CL-USER> (listp (list 1 2 3))
    T                                                                                                                                       
    CL-USER> (listp (cons 0 (list 1 2 3)))
    T


That's pretty screwed up, but having brain-damged function naming doesn't mean it's not lisp. And a few odd corners does not illegability make.


Try TXR Lisp; you will be charmed.

http://www.nongnu.org/txr


Thanks kaz, but I've already got the manpage bookmarked. I haven't looked at it in-depth yet, but it seems to be awk, but better, which is pretty awesome.


You can also completely ignore the "awk-but-different" TXR pattern-based extraction language, and just use it as a Lisp, too. The REPL is nothing but Lisp, and code stored in ".tl" files is treated as Lisp. TXR Lisp is designed to be a nice Lisp dialect on its own, not to just fill in the gaps in another language.


Well, yeah, but why have one awesome thing when you can have two? I have to finish reading the docs, so I know how it works, though.


Maclisp came from Lisp 1.5. Code was simply ported to Maclisp. Some Lisp 1.5 code runs mostly unchanged in CL.

Maclisp shared code with Common Lisp. For a lot of stuff there was a single code base at MIT for several Lisp dialects, including Maclisp. After some time Maclisp development ended and sharing ended. For example the source code for the complex LOOP macro was at one time a single file for Maclisp, NIL, Lisp Machine Lisp and Common Lisp.

People ported programs from Maclisp to CL by changing them or by translating them with tools, not by rewriting them.

There were also code bases which worked both in early Scheme and CL.

Emacs Lisp has a huge CL subset and even includes a version of CLOS. Since both Emacs Lisp and Common Lisp are coming from Maclisp, both languages are very similar anyway. Basic operators are the same, differing in various details - like dialects often do.

Common Lisp had Interlisp compatibility packages, while Interlisp was still used. There were translators, too.

Xerox had Interlisp and Common Lisp integrated in one Lisp.

Kent Pitman wrote an ISLISP compatibility package for Common Lisp.

There are compatibility packages for Portable Standard Lisp, which allow the unchanged use of PSL code in Common Lisp.

etc etc

There is zero code sharing between Lisp, Clojure and Racket. Compatibility with the Lisp 1, Lisp 1.5, Maclisp, Common Lisp, Emacs Lisp, CL, ISLISP, main line of Lisp wasn't a goal for Clojure and Racket. That's fine. They got rid of historical baggage and could design new languages.

Basic rule: if a language has LISP in its name, there is some chance that it actually is a LISP.

People usually name their language LISP something, to give the impression that the language is compatible with Lisp. They use different names, when the language is different: Scheme, Javascript, Logo, Dylan, Clojure, ...

Take a Lisp 1.5 manual, look at the function index and check which functions and special forms are provided. If most of those are absent (or doing something different), it's not a Lisp: append, atom, car, cdr, cond, cons, eq, eval, intern, list, load, map, member, pair, print, read, prog, quote, reverse, return, set, setq, trace, ...


What is the point of this genealogy? What makes a language a Lisp is (i) natural and practical homoiconicity with sexps, (ii) automatic memory management and (iii) advanced interactive development features, including the REPL, the image, restarts and hot code loading. These are the features that distinguish it and make it an advanced tool. (i) is quite particular to it, (ii) is invented for it and (iii) is a big part of the power, and for me, Lisp means a language that has (i), (ii) and (iii) in one package. Otherwise whether or not it include code from Lisp 1.5 or allow effortless porting is not all that important. For the latter, most compilers are cross-platform anyways, so one can just grab a suitable binary tarball and get going.

Clojure fails at (iii), as it just propagates Java exceptions, but I don't think that is impossible to fix, so not a fundamental flaw. JVM is too heavy, and that is a problem for some. Otherwise, it's a well thought-out Lisp with a thriving community and some cool guys and a BDFL (i.e. Hickey).

Common Lisp excels at all the three of the items I listed, plus it's batteries included and has lots of libraries around. Also, it has a standard, so that you can develop on SBCL and deploy with Clozure CL or CMU CL. It's downside is there's some historical baggage and there are no DHH or Hickey or Torvalds in the community, which isn't a downside for me, even an upside.


Those aren't what make a language a Lisp. There are Lisps without Automatic Memory Management, there are Lisps without restarts or images (although hot code loading is fairly common, and I've never seen a Lisp without a repl). Homoiconic expressions are an important part of it, but there are many lisps without macro systems.

I'm a Schemer, and Scheme IS a lisp: we don't have restarts, but we do have call/cc, and images as Lisp has them are an optimization hack in any case. And we're finally getting some well supported implementations. Yes, we have hot code loading in most implementations.

Come to the dark side, we have (lexical) cookies (encoded with the current continuation after baking, of course).


What I list is why I highly esteem lisp. I can't see ho it's advantageous to use without them.


...Well, then, maybe cast your net a bit wider? There are plenty of other reasons to use lisps.


Clojure has a primitive repl by default with lots of Java shining through. It has no image dumping. No restarts. Hot code loading is limited. Clojure even has no interpreter.


Need I mention that Lisp 1.5 had no restarts, and that image dumps are primarily a hack, and not at the core of lisp by a long shot?


Images are not a hack. They allow us to save the state of a program and use it later. My Lisp Machine at home would not even work without it. It's the software the machine boots into.

Image dumps are also a useful way to create applications or use pre-assembled program state during development. Makes faster startup times, than loading objects during start each time.

Even an editor like GNU Emacs boots up a Lisp image. Which makes it much faster than loading code at start.


They're an optimization in most dialects that support it: it's not like Smalltalk, where you can reload your app state and continue exactly where you left off.


Smalltalk doesn't do that and if it does, it's because the runtime is dumb. For example, a Smalltalk which saves and restores threads in an image, can only do that, because it does not use native threads. Various Lisp systems are using native threads. If a Smalltalk image starts, formerly open files may be opened again. If the files is not there, then nothing happens. Same for network connections.

I would be very irritated if my Lisp Machine on start would reopen all network connections... But it has a controlled restart process, where I tell Lisp what to restart on a reboot. Actually a Lisp Machine has a list in memory, where the restart tasks are kept. If the image is restarted, one of the tasks it does is to go through this list and execute the various restart tasks...


I don't know all of smalltalk's semantics for transient connections: However, when a smalltalk image is shut down, a snapshot of all objects in a live system are kept. I would assume that the VM signals a shutdown, and that would tell all the transient entities to close themselves.

Anyways, Lisp images don't work like that: a lisp image is a static preloaded environment. If you redefine some functions, exit lisp, and reopen the interpreter, those redefinitions won't be there.


Why would you exit? On a Lisp Machine you can make the image and continue working. It also has incremental images, etc.

If I save and load an image, the new definitions are still there.

What you refer to has very little to do with imags, Smalltalk keeps a database of code in files under system control. That's also how Interlisp worked.

On a Symbolics Lisp Machine I would configure the machine, load my software and create an image. After starting that image I would on login load any changes from the code bases. If this would be too long, I would save an incremental image.

The Lisp Machine had many more image related features...


Well, on a lispm, yes. However, most of us don't have lispms (as much as we might want one!), and the symbolics VM is a pain to run. In CL, you don't do that sort of thing.

In any case, at least a few Schemes have images, so it's a moot point in that debate. It's still interesting, though.


> In CL, you don't do that sort of thing

Oh, something like LispWorks has extensive support for using images, too. It has a very extensive application/library delivery mechanism based on images.

LispWorks can also save 'sessions', even automatically as periodically timed events. Even without quitting Lisp. Sessions are basically IDE states (files, listener history, lisp image, windows, ...).


Most of those things appear in scheme. Slightly different naming, but the same, more or less.

To say that there's compatability between Scheme and CL, and no compatability with Racket is ridiculus: Racket has Scheme compatability modes.

As for the language being called Lisp if it's Lisp, two of the examples you list in the "non lisp" category do so: Clojure's website claims it is a "dialect of lisp," and AIM-349 describes scheme as "essentially a full-funarg LISP."

As make no mistake, all of these languages, from Common Lisp to Clojure to Scheme, are dialects. They have the same origins, and they share many of the same ideas.


> AIM-349 describes scheme as "essentially a full-funarg LISP

That's long ago. That was kind of true in the mid 70s.

> and no compatability with Racket is ridiculus

CL has no Racket compatibility. Zero. Racket has no CL compatibility. Zero. Porting programs is a full rewrite.

> Clojure's website claims it is a "dialect of lisp,"

Best it is a new language, derived from Lisp and influenced by C++, C#, Common Lisp, Erlang, Haskell, Mathematica, ML, Prolog, Scheme, Java, Racket, Ruby.

'Dialect' in this case is the euphemism for 'fully incompatible' with 'random operator renames and shuffling functionality' where it kind of looked compatible.

> As make no mistake, all of these languages, from Common Lisp to Clojure to Scheme, are dialects.

Sure, but not of Lisp. Of those three, only Common Lisp is a Lisp dialect.

> They have the same origins, and they share many of the same ideas.

Less and less. As a starter: Clojure has no Lisp lists as basic data structure.


So scheme WAS a lisp, and now isn't? What changed that makes you think that?

And it had better not be nil/false equality. Even McCarthy says that was, if not a mistake, then an accident.

As for Clojure not having Lisp lists, its list implementation is... eccentric, but it DOES have lists.

Actually, scratch that, its list implementation, from a Lisp perspective is broken. Psychotically broken. But it DOES have one. Even if it's insane. What, will you say the same about newlisp lists?

For that matter, is Picolisp a Lisp?


> So scheme WAS a lisp, and now isn't?

EINE: Eine is not Emacs.

ZWEI: Zwei was Eine, Initially.

And, thus:

SWALANI: Swalani Was A Lisp, And Now Isn't.

A dialect waiting to be invented.

:)

What might be a recursive acronym for Scheme, though?

Oh, oh ... I just got one:

"Scheme Crams Hygiene into Every Macro Expansion."


It doesn't, for the record. Not any implementation that you'd use.

The acronym's good though, I just didn't want anybody else thinking that was true, given how many people already do.


> What changed that makes you think that?

R6RS

> As for Clojure not having Lisp lists, its list implementation is... eccentric, but it DOES have lists.

Lazy persistent sequences is the basic abstraction.


Uggghh. R6RS was an absolute mess. But I'd still say it was a lisp.

Have you looked at R7RS, the latest spec, that goes back to the drawing board on a lot of the stuff R6RS did?




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

Search: