Another issue has to do with startup time. The Clojure application bootstrap process is relatively slow, i.e. start-up might take 1 second, so it's great for server applications, but less great for Android apps or other CLI scripts that expect require instant startup. It has been approached in different ways, e.g. compiling Clojure with Graalvm native image or using ClojureScript instead of JVM Clojure. The latest solution is Babashka which provides a variant of Clojure specifically for writing CLI scripts.
Otherwise, Clojure is an excellent language which both modernises Lisp syntax significantly and implements a very well thought out standard library for doing functional programming. There are many great features, but the standout ones are probably the persistent data structures (which syntactically act both as data structures and functions) and parallelism/concurrency support. It's also very natural to do interop with the host platforms (Java, JS, .NET) and the data-oriented style of programming makes communication between backend (Clojure) and frontend (ClojureScript) extremely simple. So it's pretty much the perfect full-stack language for information systems. I would take a look at the rationale: https://clojure.org/about/rationale
Absolutely everything mentioned above in this quote is available in Common Lisp as well by just loading the needed libraries.
>Another issue has to do with startup time. The Clojure application bootstrap process is relatively slow, i.e. start-up might take 1 second,
>The main issue with Clojure is probably its tight integration with the various host platforms. When you get a stack trace in Clojure/ClojureScript, you basically get a Java or JavaScript stack trace, so there is some expectation of familiarity with the host platform.
Correct. And there are no such problems in Common Lisp. Except if you want to execute CL in a JVM, in which case the available implementation, ABCL, does take a slow time to start. Otherwise it's a great implementation.
I use Common Lisp and my main argument will be that Clojure isn't an "interactive programming" language like Common Lisp, Smalltalk (and Pharo, Squeak, Scratch) are. And, for me, this is removing one of the main, core advantages of Lisp.
Dispensing with the interactive programming features is, IMO, a step backwards in the state-of-the-art. Common Lisp ADDED all the improvements in the state of the art: Interactive programming from Smalltalk, lexical scoping from Scheme, various high performance/low level features from StarLisp and ZetaLisp, a very powerful OOP system, etc. And then, thanks to it being highly extensive, almost any feature can be added to it.
Clojure features like threading macros, immutable seqences, software transactional memory, and others, are already available in Common Lisp by just importing (loading) the respective library.
One of Clojure's main advantage is to be able to call Java libraries. But, surprise, you can do this in Common Lisp too, easily, by using the Armed Bear Common Lisp (ABCL) implementation. Which runs on the JVM too and makes the process of calling Java libs really, really easy. I have made a working example here, calling all the Swing UI library (java) from lisp:
https://github.com/defunkydrummer/abcl-jazz
It's true that Clojure has more widespread adoption in the industry and more libraries. However the library ecosystem on Common Lisp is decent.
>and you can get actual work done in it which is an advantage over some of the other options
This implies you can't do "actual work" in, for example, Common lisp. Which is not true, since there are companies that, in this very moment, are doing well paid, critical commercial work using Common Lisp.
But Common Lisp features like the condition system are already available in Clojure by just requiring (loading) the respective library. :)
As an example, see https://github.com/clojureman/special . Nice thing about this repo is that it points to other options re. conditions with pros and cons of each one.
Now that I think of it, you might also be referring to the fact that Common Lisp can load libraries through the REPL. Clojure can also do this through an alpha feature, add-lib, that you can already require in any project.
- Able to easily modify (and recompile) a function while your code is running
- Able to redefine a class and then change existing instances so they use the new class definition
- Able to sabe the complete running state of the system (the "image" of the system, including state of all variables, data, loaded libraries, compiled functions, etc) into a file so it can be restarted later, just like Smalltalk does.
- Able to inspect any stack frame at will and to restart execution from any chosen stack frame
These are just a few of the feature that Common Lisp has and that are part of what an "interactive language" is. Common Lisp brings all these features, they work seamlessly, without any sweat, working reliably and efficiently.
I do this all the time with Clojure at work. I will have my application running (web app) with two repls in emacs. One is connected to the ClojureScript repl and one to the Clojure repl. I am able to make changes to both front end code and back end code on the fly by changing a function and then evaluating the function into the repl. I can start the application when I get in to work in the morning and have it going all day a while I'm doing my work.
> Able to sabe the complete running state of the system (the "image" of the system, including state of all variables, data, loaded libraries, compiled functions, etc) into a file so it can be restarted later, just like Smalltalk does.
> - Able to inspect any stack frame at will and to restart execution from any chosen stack frame
Those are really cool features, are they in the base language or are they libraries that you would need to include?
If I may ask, are you using Common Lisp professionally? If so, what kinds of applications do you use it for? I've had this notion, as many others seem to have in this thread, that you don't really do anything professional with CL but I realize it is just ignorance on my part.
But since you can't arbitrarily inspect and restart any start frame, you don't really get the interactive programming experience.
On CL, for example, when you hit a runtime bug that is uncaught, you get the debugger window which shows not just the "stack trace" but the complete stack FRAMES that you can inspect. So let's say deep down you find where the error originated and the states of the variables.
You can then jump to the definition of the offending function, edit the function, recompile it, (optionally) change any variable on that stackframe, and then continue the execution by restarting specifically at that stack frame.
In this way, the feature of "modify a function while the code is running" becomes way more meaningful. The program evolves as it runs, as if it were a lifeform.
https://insideclojure.org/2018/05/04/add-lib/
Some of Clojure's other runtimes perform a bit better, Babashka, CLJS, CLR, GraalVM for different trade offs if you need something like a scripting language or native images etc
I'm sure CL is still quicker but there are options
Common Lisp is multi-paradigm language that is pretty bendy with regard to absorbing new ways of doing programming. It gets actual work done as well.
In Clojure, how do you usually approach problems that are shaped in an object-oriented way? In object-oriented languages, this usually includes either some sort of method calls (and therefore mutable state) or message passing (which usually involves actors, like Erlang processes). Passing all state around as function arguments is one way, but it becomes somewhat tedious as state grows large.
(let [temp (atom 0)]
(defn getter [] @temp)
(defn setter [val] (reset! temp val)))
To implement an object, you would do something more like: (defn new-object [init-val]
(let [temp (atom init-val)]
{:getter (fn [] @temp)
:setter (fn [val] (reset! temp val))}))
(def obj (new-object 0))
((:setter obj) 12)
To define interfaces, you could check whether the map/object conforms to a spec, etc.But obviously all this is not very idiomatic; in clojure you would keep those functions first-class through defn instead of tying them to the object / map, and would pass state as an argument. Something like:
(defn getter [obj] @obj)
(defn setter [obj val] (reset! obj val))
(def obj (atom 0)) ; This gives you the ability (and need)
; to explicitly track the list of
; existing objects in use lest they are garbage collected.
(setter obj 12)
If obj has structure (e.g. it is a map such as {:type :my.personal/type :val 12}) you can identify its type through the type keyval and can check conformance to a spec, etc.As it was said in the previous post, it's equivalent. It's a matter of how to organize code.
Thanks, TIL! That's equivalent to the "mostly functional" style that's doable in Common Lisp.
(and also obviously functions that map that state to HTML/CSS/SVG/etc.).
Clojure does have constructs to manage state. You don't need to shove it all into your function arguments.
Then you would need to have ways to mutate variables, so you'd need to stop using Clojure's persistent collections and instead use the ones provided by the JVM.
Overall, you can do it if you want to, it is just somewhat painful to do so and the language will fight you. This is like my who tried to write functional Python which is possible but then it is just a much worse Clojure.
(nota bene: I understand these things are hard to solve and I don't mean to throw the stone without putting my own time on the line to fix it, but that's one reason I'm not using VS code for clojure. Doom emacs with cider is still better, even if not perfect). If people know of someone I could throw money at to solve the integration for VS code, I'd be happy to do that.
If you find a good/great experience for clojure with vim keybindings, I'm all ears.
[1] https://calva.io/ [2] https://github.com/borkdude/clj-kondo/blob/master/doc/editor...