Clojure Rationale (2008)(clojure.org)
clojure.org
Clojure Rationale (2008)
https://clojure.org/about/rationale
51 comments
Jan, I've known of partsbox almost since it launched. I distinctly remember it blew my mind to read that it is a solo business. Now that's leverage. So I will take this chance to call out that partsbox and you have been inspirational through your work and advocacy (like your comment here). More power to you \m/.
Thank you for the kind words!
My advocacy is because I'm quite happy where I am, and I owe a large part of that to Clojure and ClojureScript. These tools make it possible for me to manage an application of this size and complexity.
My advocacy is because I'm quite happy where I am, and I owe a large part of that to Clojure and ClojureScript. These tools make it possible for me to manage an application of this size and complexity.
TIL that the Clojure rationale doesn't mention or motivate lazy sequences which are all over the place in Clojure.
That laziness (in collection APIs, duh) was a major reason that I stopped using the language more after 1 or 2 years of toying around. Maybe that concept was too foreign for me, but objectively lazy seqs don't compose with other language features like dynamic scoping.
Yet I'm happy I tried it, learned a bunch of stuff along the way.
Yet I'm happy I tried it, learned a bunch of stuff along the way.
They have their uses — I went through a similar process as you did, and these days I take care to realize lazy sequences in most places (for example using `into` or `mapv` instead of map), mostly to get localized exceptions in case they happen. But I am also very happy to have lazy sequences when I need them (transducer pipelines with data that doesn't fit in memory).
When you need them, you need them :-)
When you need them, you need them :-)
Oh absolutely - my critizism is about the decision to make them lazy by default.
Is the clojure user experience still a JVM stack trace on anything going wrong? As a non-JVM developer that was extremely off-putting.
I never quite bought into the atoms/transactions concurrency model, but probably wouldn't have looked so closely at hashed tries if they weren't clojure's choices and those have brought me a lot of joy.
I never quite bought into the atoms/transactions concurrency model, but probably wouldn't have looked so closely at hashed tries if they weren't clojure's choices and those have brought me a lot of joy.
I became a Clojure developer in the last year and a half without any prior experience with the JVM. The stack-trace problem is rarely an issue, with time I just got better to read the code, with Clojure as with any other language.
The Clojure concurrency model was the then-fashionable STM, which has largely not panned out anywhere. Luckily, atoms (based on a simple CAS) meet 99.9% of use cases.
Clojure also adopted the Go concurrency model with core.async adding channels to the language.
Very true. I was just replying to OP's bit about atoms/transactions.
stack traces have been much better over the past few years
You can alternatively look at Node stack traces, C# stack traces or Dart stack traces nowadays ;)
But, agreed, it has gotten better.
But, agreed, it has gotten better.
Closure was written in 2008. This rationale document is interesting because it promotes VMs as the way of the future. It is interesting to note that since 2008 the industry has gravitated much more back toward compiled systems.
What gives you the impression of the industry gravitating more towards compiled systems?
I see JS/TS going stronger than ever, Java and .NET still very strong and growing, Python again stronger than ever, WASM on the rise.
What's on the "compiled" side, apart from probably Rust making headlines on HN? What are the major products or services written in Rust? What are the unicorns with Rust secret sauce?
C/C++ is still strong, but is it expanding? Is Go even in the "compiled" camp?
I see JS/TS going stronger than ever, Java and .NET still very strong and growing, Python again stronger than ever, WASM on the rise.
What's on the "compiled" side, apart from probably Rust making headlines on HN? What are the major products or services written in Rust? What are the unicorns with Rust secret sauce?
C/C++ is still strong, but is it expanding? Is Go even in the "compiled" camp?
These are all growing and strong. My point was that new languages that we see coming out are compiled. Nim and Zig are examples. There's even languages that are a direct answer to "compiled Clojure" such as Carp and Janet, and most directly, Jank.
When was the last time you saw an interpreted language as a new language come out in the last few years?
When was the last time you saw an interpreted language as a new language come out in the last few years?
Maybe there’s more to be fixed with compiled languages than VM ones, currently.
The only thing that comes to mind is JITs everywhere and maybe that dotnet is trying to do more stand-alone compiling stuff. Other than that, completely agree.
What about GraalVM, which sports native AOT compilation?
Nowadays, we have both. Use GraalVM if you care about smaller memory footprint and faster startup time.
For the immediate, REPL-based development flow, a VM is a great host platform.
For the immediate, REPL-based development flow, a VM is a great host platform.
its a host platform, but a great one? Given that the platform lacks a lot of native features long used by Lisp systems. For example one can work interactively, dump a memory image, copy and restart it later, just continuing the the work. The JVM does not support that. The native object system is not tailored towards incremental change, class loaders are a pain, ...
Big fan of Clojure for sure. But looking at all the cool projects built on WASM, I'm not sure about the bare metal vs VM direction. There are compilers, interpreters, embedded support, interesting sandboxes (RLbox), webGPU. There are use cases from browsers to embedded, and it's a portable assembly language so you can still write with most higher level languages if you like the syntax and take advantage of all these environments to run in. The "browser as OS" idea has been around for a couple of decades now and it keeps moving closer to the bare metal.
There is already clojurescript, not much use for clojure to WASM in the browser. I guess there are or will be JVMs for WASM at the very least. And Clojurescript running on Nodejs or Deno won't be that much of a difference.
The industry has gravitated to giving up and putting everything in containers because most languages’ version and package management was so terrible.
Has it though? Erlang is still strong and Elixir is on the rise for some time.
The typical industry dev has never heard of them, never mind used them.
But then the typical industry dev works with Java or C# (both VM-based) or maybe Python or PHP (interpreters.)
The typical industry dev usually asks “What’s wrong with Java”
You mean the industry that is now hyping WebAssembly as if it was the very first of its kind, and re-inventing Java/.NET application servers?
With the cloud, Kubernetes, Docker and all the other stuff, I don't think the OS/language-specific VM dichotomy still makes sense.
"Memory and other resource management" Can people really claim they manage memory and resources with a low-level language when their 30-line YML configured cluster reallocates whole machines at a time?
"Memory and other resource management" Can people really claim they manage memory and resources with a low-level language when their 30-line YML configured cluster reallocates whole machines at a time?
Clojure compiles to jar files, that run on JVM. So, it’s both compiling and JIT in the REPL. In a weird way.
How do you define the difference? It seems it's difficult to draw a line with modern systems.
VMs still probably a better fit for dynamic languages. But modern Java doesn't really need a VM.
It was strange at the time to put a lisp on the king of OOP languages, given all the lispers so fiercely dismissive of OOP, but never has a lisp language enjoyed such a huge ecosystem of libraries available.
I use Erlang as my main language. I like that so many languages have lisp alternatives, so I can just rename the most common function calls to what I'm used to, and feel mostly at home between the different languages, other than different syntax for some data structures like maps.
I use Erlang as my main language. I like that so many languages have lisp alternatives, so I can just rename the most common function calls to what I'm used to, and feel mostly at home between the different languages, other than different syntax for some data structures like maps.
> given all the lispers so fiercely dismissive of OOP
I don't know where you are sampling your Lispers from. The only people I see blindly opposing OOP in Lisp communities are people who either (1) don't write a single line of code, or (2) know nothing about Common Lisp, the various Schemes with their own object systems, or even pause to consider what inspired Clojure's `defmulti` (spoiler: a Common Lisp library that used the Metaobject Protocol to allow for arbitrary dispatch in generic functions).
With that said, I do notice the Clojure community tends to be more against OOP than other Lispers, which I find golden because, again, the core language has things directly inspired from MOP libraries, and things like "atom watchers" are trivially an :AFTER method in CLOS, and so on.
I don't know where you are sampling your Lispers from. The only people I see blindly opposing OOP in Lisp communities are people who either (1) don't write a single line of code, or (2) know nothing about Common Lisp, the various Schemes with their own object systems, or even pause to consider what inspired Clojure's `defmulti` (spoiler: a Common Lisp library that used the Metaobject Protocol to allow for arbitrary dispatch in generic functions).
With that said, I do notice the Clojure community tends to be more against OOP than other Lispers, which I find golden because, again, the core language has things directly inspired from MOP libraries, and things like "atom watchers" are trivially an :AFTER method in CLOS, and so on.
I'm guessing it's because the creator, Hickey, is anti-inheritance, and the Clojure community as a whole is uncritical of him. I actually agree that inheritance can be tricky, but is still needed sometimes, especially for interop.
Elsewhere in Clojure, he makes his preferences the defaults (like immutability over mutability), but always has an escape hatch when necessary. For inheritance, there's no escape hatch. Best you can get is a crippled proxy.
Another issue is how mutable fields in deftype work. Mutable fields can only be mutated from class methods (protocol fns in Clojure parlance), so you can't use outside helper fns. You can add helper fns to your protocol (like an interface), but that's a public signature, not generally meant to have internal-only fns.
The joy of interop with Java from Clojure is occasionally getting to discover that there's no good code reuse in certain situations with a family of classes, and realizing, oh yeah, this is why we wanted inheritance in the first place.
Elsewhere in Clojure, he makes his preferences the defaults (like immutability over mutability), but always has an escape hatch when necessary. For inheritance, there's no escape hatch. Best you can get is a crippled proxy.
Another issue is how mutable fields in deftype work. Mutable fields can only be mutated from class methods (protocol fns in Clojure parlance), so you can't use outside helper fns. You can add helper fns to your protocol (like an interface), but that's a public signature, not generally meant to have internal-only fns.
The joy of interop with Java from Clojure is occasionally getting to discover that there's no good code reuse in certain situations with a family of classes, and realizing, oh yeah, this is why we wanted inheritance in the first place.
> For inheritance, there's no escape hatch. Best you can get is a crippled proxy
You can use gen-class here. Granted complex Java interop trends toward a point where it becomes easier to write and expose a Java library; the option is there though.
You can use gen-class here. Granted complex Java interop trends toward a point where it becomes easier to write and expose a Java library; the option is there though.
Hmm, good point. I always forget about gen-class, because I've been burned by AOT compilation too many times.
If he was anti-OOP he wouldn't have added most of CLOS infrastructure to Clojure.
If he was pro-OOP, why not add all of CLOS?
Easy, first of all, if he was anti-OOP, he wouldn't have added any of it.
Secondly, what he added is enough for doing OOP in Clojure, in fact, very few OOP languages support everything that CLOS allows for, and that doesn't make them less OOP.
OOP !== Java 1.0
Secondly, what he added is enough for doing OOP in Clojure, in fact, very few OOP languages support everything that CLOS allows for, and that doesn't make them less OOP.
OOP !== Java 1.0
All kidding aside, it's obvious that Hickey is not entirely anti-OOP. But then, that's not what I said; I said he was "anti-inheritance".
And my complaint is that unlike many other places in Clojure, there's no easy escape hatch for inheritance when you need it. `proxy` is limited, and `gen-class` involves AOT compilation, which can cause various problems.
And my complaint is that unlike many other places in Clojure, there's no easy escape hatch for inheritance when you need it. `proxy` is limited, and `gen-class` involves AOT compilation, which can cause various problems.
It is only strange for those that don't know Lisp, given CLOS, LOOPS, and "The Art of Metaobject Protocol", whose ideas gave birth to CLOS.
> given all the lispers so fiercely dismissive of OOP
note though, that Lisp was 1994 the first officially standardized object-oriented language.
note though, that Lisp was 1994 the first officially standardized object-oriented language.
I love there is a rationale, more projects should have one. What problem is the project designed to solve?
Is Rich Hickey author of this?
Originally, yes. Though he missed the opportunity to answer the question "Is it any good?" with the obligatory "Yes."
I've been using Clojure since 2008 and I've been running a single-founder business (SaaS) using Clojure and ClojureScript for the last 8 years. I could not have succeeded without Clojure.
From what I observe in online discussions, the biggest advantages are ones that young programmers do not appreciate :-)
* backwards compatibility: I don't have to deal with language-caused breakage
* fantastic environment: the JVM received bazillions of man-years of development and I get the benefits of a great compiler and a choice of fantastic garbage collectors
* mature community: no drama, just getting work done
* single language for server-side and client-side: most of my model (business logic) code is cljc, and gets used in both places
* no need for external serialization formats like JSON when sending data to the client (use EDN)
* not a single concurrency-related bug over 8 years
* eliminates most template/boilerplate code: nearly all code that I write is directly related to functionality
As for actual "language features", I also value different things than most commenters:
* macros are very useful, but I don't use them often. I have less than 20 in my entire code base.
* transducer pipelines are a fantastic tool to structure the application and promote code reuse, with great performance as a free side benefit, I feel like they are hugely underrated
* core.async is really useful once you get to asynchronous processing
There are also some nice practical advantages, like ClojureScript being compiled in advanced mode using the Google Closure compiler (a free performance boost!).
There is no "magic" to Clojure. It's just another programming language. It isn't revolutionary. It isn't hip or fashionable. It doesn't write your code for you. But it has a number of small improvements and niceties, which taken together result in a huge difference when writing real-world large applications.
Most importantly, when I look at my day to day work, I spend very little time on writing actual code, and I don't have any boilerplate or repetitive code. I spend time trying to understand customer workflows and thinking about how to model and represent these processes. In other words, GPT code generation isn't taking my job anytime soon.