
One Major Difference Between Clojure and Common Lisp - muriithi
http://gilesbowkett.blogspot.com/2015/01/one-major-difference-between-clojure.html
======
thom
This doesn't seem like a useful comparison, analysis or conclusion. Clojure is
philosophically a Lisp, but as others have pointed out, it isn't usefully the
same language as any other Lisp that preceded it or exists today. If this
particular example is useful to people who might otherwise think they could
reuse existing Lisp code, then I suppose it's saved some effort, but that just
seems like a straw man.

Either way, if you get the terminal condition of a recursive function wrong,
you will blow the stack. It's true Clojure isn't built to do TCO, but that's a
non-sequitur. The idea that you don't typically use recursion in Clojure is
ridiculous - for is a macro, it's syntactic sugar over Clojure's recursion
primitives, loop and recur. Even if you didn't want to use that, change the
nil? check to empty? and it works. I'm not going to claim that Clojure's
list/sequence semantics are necessarily the cleanest (the empty _sequence_ in
Clojure is nil, an empty list/vector/set etc is not), but they're not hard to
learn if you make any effort at all. Again, your existing Lisp experience may
or may not help you understand Clojure's data structures, but I am surprised
that this is a surprise to anybody.

~~~
nextos
In practice recursion is not something that you use that often I think. I've
always seen it as a low level construct. The filter/map/reduce idiom or,
alternatively, list comprehensions get you a long way.

Not to say recursion is not important. Without it lambda calculus is not
Turing complete. But Clojure does provide explicit TCO. Saying this is a big
difference is excessive IMHO.

~~~
thom
Clojure does constant-space recursion with loop/recur, but doesn't/can't do
TCO. I nevertheless use it a lot, YMMV.

------
lispm
To make a statement about Common Lisp from simple tutorial code is a bit much.

If one looks at music software in Common Lisp, the code is on a higher level
than building lists like that.

> Secondly, this code tries to handle lists in the classic Lisp way, with
> recursion, and that's not what you typically do in Clojure.

Neither is it done in Common Lisp.

> Those 7 lines of Common Lisp compress to 2 lines of Clojure

The actual difference is that Common Lisp uses LOOP and not FOR, and that LOOP
needs two nested LOOP forms:

    
    
        CL-USER 11 > (defun build (list1 list2)
                      (loop for e1 in list1 append
                            (loop for e2 in list2 collect (list e1 e2))))
        BUILD
    
        CL-USER 12 > (build '(1 2) '(a b))
        ((1 A) (1 B) (2 A) (2 B))
    

Two iteration forms in a LOOP don't nest, but provide iteration bindings
similar to LET* and LET:

    
    
        (loop for i in '(1 2 3) for j = (expt i 2) collect (list i j))
    

and

    
    
        (loop for i in '(1 2 3) and j in '(1 4 9) collect (list i j))
    

Common Lisp's ITERATE macro also needs nested forms, but slightly improved
over LOOP:

    
    
        ITER-USER 26 > (iterate outer (for i in '(1 2))
                         (iterate (for j in '(a b))
                           (in outer (collect (list i j)))))
        ((1 A) (1 B) (2 A) (2 B))
    
    

The author of Clojure knows these differences very well, since he was a heavy
Common Lisp user for a few years.

> But, at the same time, if you're looking to translate stuff from other Lisps
> into Clojure, it's not going to be just copying and pasting. Beyond
> inconsequential, dialect-level differences like defn vs. defun, there are
> deeper differences which steepen the learning curve a little.

That's true. Clojure is not a Lisp, but partially derived from it, with many
other influences from Haskell and other languages. It's mostly incompatible to
Lisp: software can't be shared or copied, it needs to be complete rewritten.
Thus basic Lisp literature is only of use when it's about features which got
copied. For example the book 'On Lisp' might help to understand macros in Lisp
and Clojure, whereas books like 'Practical Common Lisp' or Norvig's PAIP
aren't very useful for Clojure programmers.

~~~
davorb
> Neither is it done in Common Lisp.

LOOP is something that imho very controversial. I don't think it's fair to say
that its use is idiomatic.

~~~
wtbob
> LOOP is something that imho very controversial.

No, not really: it's as controversial as Common Lisp's Lisp-n nature (which is
to say, both of which are controversial amongst Scheme programmers writing
Common Lisp). It's a perfectly useful macro. ITERATE is a bit more
traditionally Lispy, and is an easy library away if one wants it.

> I don't think it's fair to say that its use is idiomatic.

 _Not_ using it is typically less clear. It's part of the standard; it's more
elegant than using other parts of the standard; it's idiomatic.

~~~
hga
Eh, no, it was controversial when it was being developed in the early '80s, a
lot of respectable Lispers didn't like it, thought it was too complicated and
easy to get wrong. But Common Lisp also has simpler constructs like dolist
that aren't, to my knowledge, "controversial", except of course as you note
this paradigm isn't a Scheme sort of thing.

------
hga
This is why I call Clojure a "Lisp", whereas I somewhat pedantically refer to
previous languages in this family as "LISPs", as originally coined from "LISt
Processor".

But it's still a Lisp, I went straight from old half-remembered mainline LISP
and Scheme to Clojure in a recent small web server project without
difficultly. The dynamic style of development is the same as is the typing, if
you're not using lists, then the OPs problems don't come up (idiomatic Clojure
web programming uses maps, key value pairs), the syntax is still
s-expressions, albeit polluted by arrays denoted with square brackets where it
makes sense.

And I believe the article is wrong in one sense, the JVM treats non-tail
recursion like other languages, growing the stack. It's tail call optimization
(TCO) that's the issue: mandatory in Scheme, don't know about its prevalence
in Common Lisp implementations, and it's awkward in Clojure but wasn't much of
a jump from SICP for the typical cases. And I think it might be a good idea to
require signaling when you intend to tail recurse, it's easier and much
quicker to find in compilation than when you blow the stack running it.

~~~
olewhalehunter
There's something very off about working with a lisp and not being able to
inject stateful inline expressions for prototyping. In other lisps you find
the fluid abstraction/computational nature of sexps holding up very nicely
while in Clojure you find yourself having to rewrite larger portions of
functions just to test or fix something. A lot of my turnoffs from Clojure is
that it takes away the whole "geometrical logic glue" aspect of sexps and
leaves behind what feels like a neutered stack-based language for the JVM in
lisp's clothing.

~~~
hga
Hmmmm; I haven't worked with Clojure enough, and that after a decade break
from programming and several from serious LISP programming, but ... while I
like it, I don't find it very tasteful in many ways, including that very sort
of way. It's a language I like to program in, but not one I think I'll ever
fall in love with like mainline LISP and then Scheme.

While I very much like the first class syntax for arrays, maps, and sets,
there's a _tremendous_ advantage to having one main or even exclusive built in
composite data type, e.g. I hear that a strength of Lua, which I gather has
been very successful. LISP's DNA, as well as Scheme's I'm pretty sure, was
established in the days when that was lists.

~~~
GFK_of_xmaspast
"While I very much like the first class syntax for arrays, maps, and sets"

Don't lisp advocates usually say it has no syntax.

~~~
59nadir
Clojure explicitly makes '()' and '[]' different, as well as a few other
delimiters. '(1 2 3) is a list like in Lisp tradition, but [1 2 3] is a
vector. That is the syntax that the parent is talking about.

~~~
WildUtah
Yes, specifically [:whatever] is syntactic sugar for (vector :whatever) and
{:what :ever} is syntactic sugar for {array-map :what :ever}. CL has the #()
syntactic sugar for the first and the #'acons syntactic sugar for the second.
So, in fact, Clojure is no more unLISPy than CL in the most quoted respect.

(Quote from CLtL: Many people have suggested that brackets be used to notate
vectors, as [a b c] instead of #(a b c). This notation would be shorter,
perhaps more readable, and certainly in accord with cultural conventions in
other parts of computer science and mathematics. However, to preserve the
usefulness of the user-definable macro-character feature of the function read,
it is necessary to leave some characters to the user for this purpose.
Experience in MacLisp has shown that users, especially implementors of
languages for use in artificial intelligence research, often want to define
special kinds of brackets. Therefore Common Lisp avoids using brackets and
braces for any syntactic purpose.)

------
devin
The conclusion of this article is: "Clojure is not the same as Common Lisp."
Well, yeah, it's not the exact same language. How is this surprising?

I wish the author would have spent a little bit more time researching. The
comparison is just plain sloppy. Why is Clojure's `recur` not mentioned, for
instance? This is documentation that's not that difficult to find by googling,
and is mentioned in (I believe) every Clojure book available.

~~~
jwdunne
To make a point, I thought about 'recur' and I haven't written a single line
of Clojure in my life. In comparsion, CL's looping constructs were not
mentioned, which crop up often enough.

------
raspasov
From my perspective, the three distinguishing features of Clojure from other
languages/Lisps in general are:

1\. It's a lisp, which means homoiconic, i.e. build software just like Lego
(different from non-lisps)

2\. Immutability and purity in the core library + sane, managed concurrency
via atoms, refs, core.async, i.e. write serious multithreaded code without
putting your hair on fire; helps on the front-end via ClojureScript as well
(different from most other Lisps)

3\. The JVM, which means very good performance in the general case + build
once, run everyone + a lot of libraries

------
wtbob
The Common Lisp version he has is pretty poor; I think this is more idiomatic,
and is two lines as well:

    
    
        (defun build (list1 list2) 
           (loop for x in list1 append (loop for y in list2 collect `(,x ,y))))
    

or:

    
    
        (defun build (list1 list2) 
           (mapcan (lambda (x) (mapcar (lambda (y) `(,x ,y)) list2)) list1))
    

Still more verbose, of course, but not at all bad.

------
treerex
Lisp != Common Lisp. The very thought that you can cut-n-paste code from one
Lisp dialect to another is daft. The author obviously didn't take the time to
acquaint themselves with the basics of Clojure.

The lack of TCO in JVM hosted languages is besides the point.

It is also possible for the Clojure compiler to do TCO in certain cases: Rich
Hickey made the conscious decision to not do it.

~~~
EdwardDiego
You can use the recur special form to enforce recursion from the tail position
in Clojure.

~~~
lispm
True, but that's not TCO (or tail call elimination). That would mean that
every single tail call is optimized to eliminate that stack overhead.

    
    
        (defun foo () (foo)) ;<- tail call
    
        (defun bar () (bar)) ;<- tail call
    
        (defun baz (a b) (+  ;<- tail call
                            (* a 2)
                            (* b 3)))

------
codecurve
For all the gripes surrounding the lack of TCO on the JVM, Clojure really does
provide a great set of tools to deal with iteration in a functional way. It's
a rare thing when I need to fall back to using loop & recur.

------
kyllo
This is not a Clojure problem, it's a JVM problem--no tail call optimization.
Armed Bear Common Lisp has the exact same limitation.

~~~
eru
And didn't tail call optimization only become standard in Lisps with the
advent of Scheme?

In any case, at Standard Chartered we didn't have tail call optimization with
our Haskell dialect either. It didn't matter too much in practice, because you
should be using combinators anyway. And when you are calling foldr or map, you
do not care that somewhere hidden away they are implemented with a loop in
C++, as long as they behave right.

~~~
jfoutz
Yup. The CL spec does not require TCO. Some implementations only do TCO on
self calls, so no mutual recursion.

And really i think the "Classic" lisp way to loop is loop, not recursion.

Scheme forces the point and requires full TCO.

~~~
lispm
Most CL implementations will do more TCO than on self calls or mutual
recursion. Most support full TCO, with language limitations.

The ones that don't provide any form of TCO are old Lisp Machine
implementations and ABCL on the JVM.

SBCL, OTOH, provides full TCO.

Some older overview about Common Lisp implementations and their TCO support:

[http://0branch.com/notes/tco-cl.html](http://0branch.com/notes/tco-cl.html)

~~~
ohyes
I seem to recall it depending on compiler settings whether sbcl does tco.
Which means you probably don't want to rely on it in general unless you are
okay being locked to a specific implementation and specific optimization
settings that may-or-may not seem magical to the uninformed user.

~~~
lispm
There is Common Lisp software which needs TCO and only runs in TCO supporting
implementations.

------
kzhahou
I'm not a clojure developer, but I'm intrigued.

I was gonna post the question "Why should I learn clojure?" but that's easy to
google for and get good articles.

I was then gonna post "What's a good book for learning Clojure?" but I can get
that on Quora.

So my real question is: what can I read about Clojure that will get me up to
speed on its unique awesomeness? I don't need a tutorial that shows me how to
add two ints or invoke a function. Show me the good stuff!

~~~
brudgers
In my opinion, Halloway's _Programming Clojure_ is a good beginner's book
because it hits the right mix of Clojure newbie with experience programming.
Among free online tutorials, Aphyr's _Clojure from the Ground Up_ is my
recommendation. A little deeper into the language, Fogus's _Joy of Clojure_
hits more technical topics.

~~~
lispnewb
I'd recommend against Programming Clojure and instead recommend Clojure
Programming from O'Reilly.

I read a fair amount of both and the former explains the subject matter too
superficially IMHO. +1 for Joy of Clojure which is a nice read in parallel.

~~~
brudgers
That was my impression of _Programming Clojure_ at first, it seemed rather
lightweight. What I came to believe is that Halloway's presentation focuses on
simplicity but achieves reasonable depth. [1] The high level of accessibility
reflects Halloway's background in the training industry and the his expertise
in Clojure. His book is efficient in the same manner as Clojure.

 _Joy of Clojure_ is a good book. It goes deeper while assuming more of the
reader. The technical detail is useful and interesting, but for me, the
narrative seems a bit less cohesive [disclaimer: I have the first edition, not
the second]. I haven't read _Clojure Programming_.

[1]: Edit. For example the running exercise is porting a Java build system to
Clojure. That's full on software engineering, not a let's-pretend.

~~~
lispnewb
Thanks for the hint, I'll check the build system example.

I focused on Programming Clojure as it's almost half the size of Clojure
Programming and I want to get to other, more focused clojure books (Reactive
Programming, Macros). But for me, personally, the examples given so far (I've
made it at least through the State-chapter) weren't well explained. I'll see
how Clojure Programming holds up in that regard but so far many explanations
have been better IMHO.

------
PuercoPop
His build implementation uses tail recursion which is not preferred in CL. One
can build in a similar way that as shown at the end.

    
    
        (defun build (list-1 list-2)
          (loop
           :for elem-1 :in list-1
           :for elem-2 :in list-2
           :collect (list elem-1 elem-2)))
    
    

A much bigger difference imho between Clojure and Common Lisp is that the
former is built on abstractions, conj, while the latter is built on concrete
cons cells. There are more other significant differences but I don't want to
flame/argue.

~~~
EdwardDiego
Is it even recursing in the tail position though? Reading the code it looks to
me like the recursion result is used as an argument to a subsequent function
call (append).

~~~
rachbowyer
Spot on! The code fails as the author has used nil? rather than empty? The
recursive call is not from the tail position so tail call optimisation is not
possible in any language.

~~~
PuercoPop
> tail call optimisation is not possible in any language.

Afaik it is possible in racket. Read the note at the end here:
[http://matt.might.net/articles/lexers-in-
racket/](http://matt.might.net/articles/lexers-in-racket/)

------
nemoniac
The reason why some people say that Clojure isn't a lisp is that some of its
design decisions such as those mentioned in the blog posting detract from the
essence of lispiness. It goes too far to say that it's not a lisp but it's
certainly less lispy than Common Lisp or Scheme.

------
dunkelheit
What is the name for illustrating the point with deliberately unrelated
example? His clojure code fails because he did not swap nil? for empty? (and
he even states this in the article) but he then uses it to illustrate the
absence of TCO in clojure.

~~~
TazeTSchnitzel
nil? is a pretty big thing. Is Clojure even a Lisp if nil is not the empty
list?

Edit: Jesus Christ, Clojure doesn't even have conses. Why the hell do people
call this a "Lisp"?

    
    
      > (= () nil)
      false
      > (('a . 'b) . ('a . 'b))
      java.lang.RuntimeException: Unable to resolve symbol: . in this context
      > (cons 'a 'b)
      java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol

~~~
dewitt
Sure, it does have cons, but your second argument wasn't a sequence. Try:

    
    
      > (cons 'a ['b])
      (a b)
    
      > (doc cons)
      -------------------------
      clojure.core/cons
      ([x seq])
      Returns a new seq where x is the first element and seq is the rest.

~~~
TazeTSchnitzel
That's not a cons, that's a linked list. As tokenrove mentions, a cons in a
Lisp is just a tuple (`cons` is the name of the type and the function
constructing it). It can contain any two values.

------
Grue3
I might be wrong, but the biggest difference is that lists are immutable in
Clojure which makes some data structures which are based on shared conses
difficult to construct, or less efficient.

