103

The intent of my question is not to start a flame war, but rather to determine in what circumstances each language is "the best tool for the job."

I have read several books on Clojure (Programming Clojure, Practical Clojure, The Joy of Clojure, and the Manning Early Access edition of Clojure in Action), and I think it is a fantastic language. I am currently reading Let Over Lambda which mostly deals with Common Lisp macros, and, it too, is a very interesting language.

I am not a Lisp expert (more of a newbie), but this family of languages fascinates me, as does functional programming, in general.

Advantages of Clojure (and disadvantages of "others"):

  • Runs on the JVM.

    • The JVM is a very stable, high-performance language environment that pretty well meets Sun's dream of "Write once, run [almost] anywhere". I can write code on my Macbook Pro, compile it into an executable JAR file, and then run it on Linux and Microsoft Windows with little additional testing.

    • The (Hotspot, and other) JVM supports high-quality garbage collection and very performant just-in-time compilation and optimization. Where just a few years ago, I wrote everything that had to run fast in C, now I do not hesitate to do so in Java.

    • Standard, simple, multithreading model. Does Common Lisp have a standard multithreading package?

    • Breaks up the monotony of all those parentheses with [], {}, and #{}, although Common Lisp experts will probably tell me that with reader macros, you can add those to CL.

Disadvantages of Clojure:

  • Runs on the JVM.
    • No tail recursion or continuations. Does Common Lisp support continuations? Scheme requires support for both, I believe.

Advantages of Others (Common Lisp, in particular) (and disadvantages of Clojure):

  • User-definable reader macros.

  • Other advantages?

Thoughts? Other differences?

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
Ralph
  • 31,584
  • 38
  • 145
  • 282
  • 17
    personally i like one kind of parentheses ;) looks like "cleaner" code – Moe May 15 '11 at 12:25
  • 3
    From what I read on your Advantages list I figure you might maybe also like Erlang www.erlang.org – Peer Stritzinger May 15 '11 at 12:28
  • 4
    Clojure does support explicit tail recursion via the "recur" special form. This enables you to get all the benefits of tail recursion provided you explicitly ask for it (the only exception is that it does not currently support mutual tail recursions between multiple functions). – mikera May 15 '11 at 13:44
  • 1
    Clojure also supports continuations, at least in the sense of "continuation passing style". You are correct that it does not have first class continuations. see http://stackoverflow.com/questions/1173133/continuations-in-clojure – mikera May 15 '11 at 13:46
  • @mikera: Tail-recursion on one function. Two functions calling each other have to be done with "trampolining", which is sort of kludgy (but elegant in its own way :-)). – Ralph May 15 '11 at 13:52
  • @Ralph yes I agree trampolining works but is a bit kludgy... you'd need JVM support for tail recursion to make this work neatly (which I hope will appear sometime soon!) – mikera May 15 '11 at 14:02
  • It can be mentioned that CL seems to be more supported. When I develop in Clojure I don't get warnings, I have to use 3rd-party libraries to be able to load other libraries at runtime, and so on. I prefer Clojure but I wish it was more supported, perhaps native too, and at the very least be a language and not just an implementation (so to speak). CL has also more capabilities such as CLOS, restarts, etc. and it also supported reader macros which I personally think is fantastic. – MasterMastic Jun 22 '16 at 12:17
  • Here is a link with list of technical differences between Clojure and CL on official site https://clojure.org/reference/lisps. (I'll just leave it here) – Zapko Aug 31 '18 at 07:46
  • When I saw this question, I was really hoping that "runs on the JVM" was listed as both a pro and a con. Was not disappointed - upvotes from me – iCodeSometime Jul 02 '19 at 19:56

4 Answers4

58

My personal list of reasons for preferring Clojure to other Lisps (p.s. I still think all Lisps are great!):

  • Runs on the JVM - hence gets automatic access to the fantastic engineering in the JVM itself (advanced garbage collection algorithms, HotSpot JIT optimisation etc.)

  • Very good Java interoperability - provides compatibility with the huge range of libraries in the Java/JVM language ecosystem. I have used Clojure as a "glue" language to connect different Java libraries with good effect. As I also develop a lot of Java code it is helpful for me that Clojure integrates well with Java tooling (e.g. I use Maven, Eclipse with Counterclockwise plugin for my Clojure development)

  • Nice syntax for vectors [1 2 3], maps {:bob 10, :jane 15} and sets #{"a" "b" "c"} - I consider these pretty essential tools for modern programming (in addition to lists of course!)

  • I personally like the use of square brackets for binding forms: e.g. (defn foo [a b] (+ a b)) - I think it makes code a bit clearer to read.

  • Emphasis on lazy, functional programming with persistent, immutable data structures - in particular all the core Clojure library is designed to support this by default

  • Excellent STM implementation for multi-core concurrency. I believe Clojure has the best concurrency story of any language at the moment (see this video for more elaboration by Rich Hickey himself)

  • It's a Lisp-1 (like Scheme), which I personally prefer (I think in a functional language it makes sense to keep functions and data in the same namespace)

ntalbs
  • 28,700
  • 8
  • 66
  • 83
mikera
  • 105,238
  • 25
  • 256
  • 415
  • 2
    +1 for the STM. It is, by itselft, enough to justify using Clojure. – André Caron May 15 '11 at 14:11
  • 2
    You can still get STM using library CL-STM. – Mike Manilone May 25 '13 at 13:03
  • 2
    @AndréCaron only if you need it. –  Jun 03 '15 at 07:33
  • 1
    If you wanted to write a simple webapp, and host it on, say, a cheap $5/ month host, that is obviously not possible with Clojure due to JVM, correct? – Hexatonic Mar 18 '16 at 19:37
  • @Hexatonic I'm not very experienced but it's hard to believe a machine in use these days wouldn't have a JVM. – MasterMastic Jun 19 '16 at 09:00
  • Clojure beating Erlang concurrency model ? – loxaxs Feb 07 '18 at 00:59
  • @loxaxs beat the Erlang concurrency model? IDK. But I can say pretty easily having messed around with both that clojure is easier to learn and seems to have vastly better tooling for development/monitoring/etc. Erlang's syntax is kinda esoteric and not terribly intuitive (at least not to me). – Jared Smith Aug 29 '18 at 23:54
25

An important difference between Clojure and Common Lisp is that Clojure is more prescriptive about functional programming. Clojure's philosophy, idioms, and to some degree language/libraries strongly encourage and sometimes insist that you program in a functional way (no side effects, no mutable state).

Common Lisp definitely supports functional programming, but it also allows mutable state and imperative programming.

Of course, there are a number of benefits to functional programming, in the area of concurrency and otherwise. But all else being equal, it is also good to have the choice of which approach you want to use for each situation. Clojure doesn't completely prohibit imperative programming, but it is less accommodating of that style than Common Lisp.

Charlie Flowers
  • 17,338
  • 10
  • 71
  • 88
  • 3
    @Charlie Flowers: I believe that in Common Lisp it is possible to program in a "purely functional" style (persistent data structure support, etc.), but requires discipline. Correct? – Ralph May 15 '11 at 12:43
  • 3
    Just a clarification on "no side effects, no mutable state" - Clojure does have mutable state (refs, atoms, agents etc. are all mutable) but requires that you access it in a controlled way (i.e. via the STM mechanisms and related transactional update semantics) – mikera May 15 '11 at 13:40
  • 5
    @mikera: except that Clojure relies on the use of Java libraries to be usable, and all those libraries require imperative-style and are full of side-effects. I've found the bindings with Java to be a poisoned gift... – André Caron May 15 '11 at 14:09
  • 2
    @Andre - sure, if you decide to use a library that requires mutable state and imperative semantics then you have to manage this. This is no different from if you accessed such a library from any other language. But you have two decent options: a) Don't use such libraries - you can write perfectly good code in pure Clojure or b) wrap the complexity of interfacing with these libraries in a nice Clojure-style functional interface, which is usually easy with macros or agents etc. Overall, I've found the ability to harness Java libraries a much bigger benefit than it is problem. – mikera May 15 '11 at 14:16
  • 5
    @mikera: the libraries do have a benefit. I'm just pointing out that using Java libraries (this is one of Rich Hickey's primary goals for the language) really goes against the "more functional than other lisps" aspect of Clojure. My comment was intended to mean: "unless you rewrite/wrap these libraries, you will get imperative-looking code and won't benefit from Clojure's nicer parts". – André Caron May 15 '11 at 15:32
25

Keep in mind that Clojure is a language and an implementation (usually on the JVM). Common Lisp is a language with more than ten different implementations. So we have a category mismatch right here. You might for example compare Clojure with SBCL.

Generally:

  • a version of Common Lisp runs on the JVM: ABCL

  • most other Common Lisp implementation don't

  • most CL implementations have multitasking capabilities, a library provides a common interface

  • Common Lisp has syntax for arrays. Syntax for other data types can be written by the user and are provided by various libraries.

  • Common Lisp supports neither tail call optimization nor continuations. Implementations provide TCO and libraries provide some form of continuations.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
10

Here's a good video with a comparison of Scheme (Racket mostly) and Clojure.

To be fair, Racket has syntax sugar (additional reader stuff) for data types too (#hash, #, square brackets, etc.)

Plus, Clojure's only way to make a proper tail call is to use recur, that's the downside of compiling to JVM.

Note that recur is the only non-stack-consuming looping construct in Clojure. There is no tail-call optimization and the use of self-calls for looping of unknown bounds is discouraged. recur is functional and its use in tail-position is verified by the compiler. (Special Forms).

Daniil
  • 637
  • 4
  • 12