The way the book is presented as building a series of small libraries that you then leverage to make a larger more complex application is absolutely wonderful. The best done “practical” book I’ve read.
Common Lisp is just not very practical compared to Clojure though.
[Comment removed by author]
The Java ecosystem is vastly healthier than Common Lisp’s (CL), it’s very easy to connect your Clojure code to it,
In case you didn’t know, there ABCL, which allows interoperation with Java, calling Java from CL and vice-versa.
and has out of the box answers for many things like networking and most especially concurrency that became significant after CL was frozen in amber by standardization.
Most CL implementations provide threads and sockets out of the box. For concurrency (and parellism) there are plenty of answers in FLOSS CL. Including STM
For that matter, there’s a big difference between a frozen language,
What is frozen is the standard not the language. The CL was designed to be extended. And implementations are free to do so. For example SBCL adds the :syncronized keyword argument to ensure for thread-safety concurrent access. CCL’s hash-table is thread-safe by default, features that are not mentioned in the standard.
such a big language at that (rumor has it Symbolics forced a lot of Lisp Machine Lisp’s complexity into the standard),
I’m failing to how you are connecting being a ‘big’ language with being practical. Even more what is the purpose of adding hearsay with blanket statements about the standard. Is being a ‘big’ language opposed to practicality? What metric do you use for ‘big’? If you mean the number of variables and function in its standard library (1064) that can hardly be counted as big. Or are you referring the number of special forms? Btw last time I checked, ‘batteries included’ is helpful when developing software.
and one who’s efforts are split between many different implementations,
Because software development, as pretty much everything else in life are about trade-offs, which trade-off to take depends of the context. Multiple implementations is a plus.
and one still at the BDFL stage with only a couple of major implementations last time I checked (the death of the Clojure project I was working on two years ago, and Real Life distractions for the last year or so have had me out of the loop, which is about to change),
Certainly a hype master is good for adoption but has little relevance for practicality. Btw my intentation is not to throw shade to Rich Hickey, who is a smart person and has made excellent choices in the design of Clojure; and popularity is helpful for a language. For instance it helps the availability of libraries and documentation.
and that’s continuing to evolve.
One doesn’t need a standard to evolve/change CL, it is part of the correct decisions by the Standardization committee. Macros are one way to enable the programmer to extend the language, but CL provides more features in that front, like reader macros and the oft underused meta-object protocol.
For example the threading macros extend the language past the standard. Or you want to provide syntax for function composition? No need to change the standard for that.
So your argument for practicality sums up to: Clojure has more libraries, which is a true statement. But being practical is more than just about libraries. And also the libraries of the ecosystem are not to be measured just from a quantitative point of view. Does having one more library for JSON processing more useful? Certainly Clojure has more libraries but Common Lisp has more than enough.
The standard itself is a result 10 years of practice writing software in Common Lisp and it shows.
Keyword arguments (and other features of lambda-lists, such as rest/aux) trade-off function call performance to help extend function with new features after they were written minimizing changes in an existing code base.
Having any atom other than nil be and applying the philosophy of returning useful values is a practical trade-off to help the application developer while putting more pressure on the garbage collector.
Another example of a practical choice is the often cited lack of hash-table (readable) syntax. Constant factors matter, when one can type the contents of the hash-table probably one shouldn’t be using one, plus printing huge readable when working on the REPL is useful.
Having the standard method combination modeled after a middle-ware architecture that fits most of the use causes but providing other method combinations, such as append for when one wants to aggregate data from different sources and filter it (in the :around method) is another practical choice (batteries included).
Loop is practical almost to the point of fault, ~80% of everything that one wants to write a loop for can be written succinctly with it. In comparison Clojure’s loop/recur doesn’t strike me as the most practical of the two.
Being a Lisp-2 is also another inelegant sacrifice in favor practical choice, although it takes time to appreciate. And the list goes on and on, because Common Lisp is not about achieving some kind of bullshit hacker enlightenment, as it is often touted, Common Lisp is about writing code. It is one of the most practical languages out are.
I’m under the impression that you are confusing popular with practical. Clojure is indubitably more popular, but certainly not much more practical than Common Lisp, if at all.
Furthermore, it irks that some clojurians seem compelled to chime in on stories mentioning CL and parrot the same rote and incorrect talking points about CL, which they don’t seem to have used much, if at all. No need to throw shade to shine. Clojure is a well designed language from what I remember. Having quasiquote fully qualify the symbol and having maps being callable as an access mechanism are both excellent design choices. Another good idea behind Clojure is to build everything around abstractions (conj). Because I don’t know much Clojure I’m not qualified to comment about its design mistakes, but note that there are people who having used both, Common Lisp and Clojure, consider Common Lisp to be better designed
I sometimes joke my favourite programming language is LOOP instead of CL. I literally never use DOLIST, DOTIMES or any of the other iteration constructs besides LOOP and when I see them used in other CL source I’m like “why? there’s LOOP”.
Haha, yes, by now I have accepted that I am mostly an imperative programmer.
I blame childhood BASIC exposure.
These are the facts we agree on:
note that all of the points above are not intrinsic properties of a language
Your thesis is Clojure is significantly more practical. Not if it is better, more functional or more popular.
Your reply was not to the point:
I did, but I’ll concede that due to the volume of my reply and poor composition it is likely that I wasn’t very clear:
Being more popular is unrelated to how practical a language is. PHP is more popular than Clojure, does that make PHP more practical than Clojure?
Having having libraries, extrinsic to the language, is a practical concern, but not the only practical concern. Furthermore I posit more than availability of libraries being the matter is the lack of the libraries that would be a practical impediment and stated that Common Lisp has enough libraries so that it is not a concern.
Afterwards I gave examples of how the intrinsic properties of Common Lisp’s design help the programmer to write code; that is how Common Lisp’s design is related to the practice of writing code in it.
JVM Clojure’s near trivial ability to call and use JVM hosted code and data would be germane
If the thesis would be that Clojure is practical, yes. When compare it with Common Lisp, which has implementations that have that feature as well, no. It is not a substantive difference.
Clojure being a hosted language, which very much pertains to real world practicality.
It does, it affects the extent to which is semantics are well defined, apparently Nowhere does Clojure promise that all user errors will become exceptions. Sacrificing catching errors early in lieu of ease of implementation doesn’t strike me as the practical choice but a (necessary?) compromise.
It’s not a choice at all,
It is a choice, backwards compatibility was one of the factors. It wasn’t the only one. Lisp-2 vs Lisp-1 is a trade-off, neither of the two approaches are strictly better.
Heh, nowadays that’s the job of Scheme.
Racket, gambit, chicken, etc would disagree. Furthermore it is of poor taste to divert the attention by jokinly put down a different community. The whole enlightenment idea is misguided, probably playing into the idea that one should ‘work smarter not harder’. That if you achieve enlightenment you’ll join the ranks of the ‘10x’ programmers. Furthermore it is a misrepresentation of the actual concept of enlightenment, as the proverb says: “Before enlightenment, chop wood and carry water. After enlightenment, chop wood and carry water.” But that is neither here nor there.
close with the point that nowadays I view the Lisp family of languages from a “how to they support functional programming"
Unless you are equating functional programming with practical software development that is besides the point.
So things like the acknowledged wonders of LOOP are not particularly germane to me
Again, as mentioned by others, it depends on the context. Things are not better in a vacuum. Everything is about trade-offs. If you are concerned with writing code in a way that allows parallelization, then yes LOOP is of no use. If you are not, not an uncommon case, then it very useful.
the why of that especially pertains to the issues that arise from not starting your dialect and/or implementation on day one with native threading in mind
You are confusing not including threads in the standard with not designing with them in mind. Implementations specify the semantics for threads according to their needs. Standardizing threads would have been a mistake as different OSes/architecture have different needs.
Also keep in mind that designing a language with parallelizing work in mind does not require one to talk about OS threads. E.j Guy Steele’s conc
Btw since the early 90’s Henry baker warned that functional programming was not the best way to achieve high performance parallel computation, so you may consider checking our rust if that is your concern
Here I can only confess to not being current with it. But
Note that this was an observation of a kind of comment not limited to yours, with that said you are missing the point, which was the need to derail the conversation and make it about Clojure vs Lisp or even just about Clojure. Common Lisp and Clojure are not similar languages, so why chime in about Clojure? Is is because of the parens? Common Lisp is much more similar to Smalltalk in my experience but I don’t see Pharo users hijacking every conversation about Common Lisp. Note that to compare both languages adequately one must be familiar with both, not just Clojure. Which is why I mentioned that they don’t seem to have used Common Lisp, an an aggravating situation.
Btw while looking for Ken Pitman’s paper I came to this old comment, if that is in fact you it seems to support the idea that you haven’t had much experience with Common Lisp.
I speculate that the apparent need of some clojurians is due to the fact that Clojure markets itself as a modern Lisp, so they seem think that if the other Lisps are not perceived archaic and obsolete then Clojure won’t be percevied as better the choice.
I’ve already spent more time that I care for arguing over this and I’m sure you have done so as well. The readers can make up their mind.
By what measure is Clojure more popular than Common Lisp? I’m not saying it is or isn’t, just wondering what measure is used to make this claim.
You can argue about community health, but the old language standard isn’t really a problem. Part of any lisp’s strength is its extensibility which reduces the need for a large standard library. Take threads for instance: the CL standard has no concept of threads, but there are high-quality libraries providing both the basics and STM. Incidentally, that STM library is better than Clojure’s both in interface (implements RETRY for blocking transactions) and implementation (supports hardware transactions where available, greatly helping performance), and Clojure devs claim their STM as a selling point. Similarly, there are libraries for efficient immutable datastructures, sockets, etc. So the argument that the old standard has somehow “frozen” CL is suspect both in theory - you have the final say in the language - and in practice - CL devs seem to be getting along fine.
“trying to graft STM onto an imperative language just doesn’t work in practice”
Which would come as a surprise to GemStone Smalltalk which has had multiuser, distributed STM and GC for 30 years.
Smalltalk-80 does not provide STM. GemStone Smalltalk is ST-80 expanded for multi-user, distributed, ACID STM-based transactions with support for reduced-conflict “monotone classes”.
Microsoft made the mistake of trying to make their STM transparent, and they abandoned their effort when it became clear just how many problems there were with this approach; see the retrospective, though it sounds like you’ve already read it. Clojure and CL’s STMX both avoid this issue with explicit transactional variables. The other issue with STM is interaction with IO and non-STM synchronization, and Clojure has this issue just as much as CL does.
And let us posit CL’s STM is superior; that doesn’t make Clojure’s STM any less of a selling point for the language.
Absolutely, but if STM’s a good selling point for Clojure, it would seem better STM should be a better selling point for CL.
…and most if not almost all implementations not starting with native threads in mind, things can get gnarly for a quite a while.
This might have been a problem in the past - I couldn’t say because I was not writing Lisp in the 90s - but every commonly-used CL implementation has has literally 20 years to figure this out, and all of them have. Threading support in SBCL (I know firsthand) and others (I’ve heard) is currently equal to any other systems programming language in common use, with the caveat that no CL implementation I know of supports lightweight user-scheduled threads. Which isn’t great, but Clojure’s in the same boat here. Claiming that CL has inadequate support for threading at this point is basically FUD, which I’m sure isn’t your intention, but please don’t make sweeping suppositions about problems Common Lisp could have when those problems have when those problems have already been cleanly solved.
Both have pro’s and con’s. Which is better is context-specific and/or personal preference.
CL has multiple implementations, multiple commercially supported and mature. Multiple platforms, including mobile. And so on.
I’m not going to get into a back and forth on this, but there is no clear “winner” for all situations. Teams should take care to investigate and evaluate for their situations.
As someone who’s never used Clojure, but who is familiar with the concept and fairly experienced with Lisp: how is the impedience mismatch between Clojure and the rest of the Java ecosystem?
I feel like a mostly functional language with a mostly object-oriented standard library would just feel…weird.
And if you’re a True Lisper, the substitution of array syntax, square brackets (‘[’ and ‘]’) in places where they do make sense like function definition argument lists and lets is annoying, tedious for my style of editing, and not pretty, but so very many people reject LISPs because all the major syntax is parens hopefully justifies it.
Did you mean “places where they don’t make sense?” Function definition argument lists and let binding forms are generally read sequentially and in their entirety, rarely have more than a handful of elements, and don’t really require random access–in other words, they are perfect candidates for the use of a list, and the use of the ill-suited vector for these things is a constant irritation when writing Clojure. It’s so clear that the less-appropriate data structure was chosen purely for the syntactic sugar it affords–and to my eye (admittedly formed by years of writing Common Lisp) this syntax only makes it uglier.
I think you’re making some broad and not necessarily correct assumptions here. The corpus of common lisp code has been around and maturing since before Java was a twinkle in Gosling’s eye.