Clojure is a recurring gravitational pull for me. I haven't had time to properly explore yet but I will before end of year.
It's not a "first" language like JS, Python, Java, C#, Ruby etc. so the community appears to benefit from a higher than average level of skill. In the same way that i imagine Erlang or Elixir might enjoy the benefit of more capable than average users.
The fact that entire paradigms can be implemented as libraries is attractive to me - it suggests to me that the Python 3000 or C++11 problems are unlikely to affect Clojure.
For example, Go is a language I find interesting. The whole CSP concept makes a ton of sense to me in writing enterprise software - mainly from a "keeping the architecture cheap to own and change over time" point of view. I appreciate this isn't where CSP was targeted but it lends itself very well IMO to solving this problem because you could decouple components and grow them independently so effectively.
Go is an entire language but core.async is just a library in Clojure, they didn't have to change the language to implement this which is mind blowing to me.
Another example is types. Only some of the code I write actually benefits from types AND most type systems in "enterprise" languages are pretty anaemic. Typing is also really hard to do well, like really really hard. You had a problem, you adopted typing and now you have two problems is generally my thinking here.
Scala was to be my saviour here but that missed the mark quite spectacularly and the compiler is still ridiculously slow today but then along came TypeScript which is pretty darned awesome IMO.
Thanks to TypeScript and modern Python i had learned that i wanted "optional typing" and i was undecided about type erasure at runtime - i don't immediately see a problem with it, i've written probably thousands of lines of Java with generics then used reflection at runtime without losing sleep, but it fills an awkward space in my head - to invest in types but only benefit at compile time seems like a miss.
In Clojure i can follow the TypeScript model but actually take it further to runtime.
This post is too long already. Long way to say Clojure appears very interesting.
The clojure community has robust tooling around types and similar mechanisms. There is core.typed, but I honestly don’t know of many who use it in production.
More recently, clojure.spec was released (https://clojure.org/about/spec) which I see the community starting to coalesce around. It’s a nicely-designed way for doing validation and property verification at runtime, and imo avoids the “awkward space” problem you mentioned. By creating specs, you get validation, nice errors, and generative testing (almost) for free. Because Clojure development is often repl-driven anyway, there’s not a huge benefit in having compile-time types, at least if you subscribe to the philosophy behind clojure.spec.
I like Clojure quite a lot---for a while, it was actually my main language.
Ultimately, however, I largely gave it up because the library ecosystem just doesn't feel quite right to me---at least at the time I was writing a lot in it (2016-7ish), most of the popular libraries seemed to have a very strong refugee-from-Java sense to them, as in, very influenced by ideas more commonly seen in oo-land. For example, people loved to use (and, more importantly, bundle in other projects) libraries like component/mount that attempt to implement ideas like dependency injection and other design pattern/architectural concepts that just... don't really feel lispy. I remember trying to build a fairly simple web application with one of the main frameworks and just having no clue which piece of the hyper-complicated and indirect code that the lein template built for me I'd need to change to debug some problems with the database. Alas.
(To be clear, I'm not saying this is wrong! It just wasn't for me.)
I’ve been playing a lot with the idea of just using ClojureScript for everything, both backend and frontend.
Shadow-cljs [0] makes interop with the NPM ecosystem a breeze, and ClojureScript is stable, mature, and feature-complete in its own right.
Although you lose out on some of the features of the JVM (threading), you get access to a lighter weight runtime, and the massive world of JavaScript libraries (which don’t bring much OOP baggage with them).
I’m not sure how viable it is long term, but so far it’s been fun, and pretty trivial to write simple wrappers over JS libraries (when repeated direct interop doesn’t make sense).
Somebody else in here mentioned that Clojure isn’t a “first” language, but I think if there was an “easier” (yes, I said easier, not simpler) path to get into it, more people might pick it up as their second language.
I can only say glowing things about Clojure. If you have any interest at all, I strongly, strongly encourage you to try it out. braveclojure.org is a decent start, and it’s also fun to try out the problems at 4clojure.org.
>You had a problem, you adopted typing and now you have two problems is generally my thinking here.
When you are working with types, it's like you are working out a complex puzzle. Each time you've solved a part of the system you've created more puzzle pieces in other parts. Solving this meta-puzzle feels nice and productive, especially once you get the hang of it. But it's not really solving the original problem, it's just managing the accidental complexity.
Sometimes it feels hyper productive - when you’re working out the practical details of how to model your problem, typing feels like you have a smart assistant helping you think, or at least organise your thoughts more clearly.
When you’re changing some code that’s been around for a while, or writing adapter type code, or any kind of SERDES type operations, it begins to feel less like helpful guard rails and more like unproductive yak shaving. After the initial creation, most of my code changes fall in the latter camp.
I definitely think having a comfortable way to say “here, in this code, i want types. Over here, i don’t care” is a control missing in many languages. You can certainly fake it (e.g. string typing or everything is object type) but not comfortably like for example in TypeScript.
For me the most interesting part is how it gets rid of the JVM startup time.
So there are two parts to the solution: The first part is Small Clojure Interpreter (sci) which is a "(subset of) Clojure written in Clojure", so kind of McCarthy for Clojure. A cool project in itself but it still needs a VM to run.
So here is where the second part comes in: the GraalVM produces native binaries from various things, including Clojure code and SCI is simple enough to work with GraalVM.
It's cool how these two pieces of tech combine together to create something greater than the sum of the parts.
This high startup time in Clojure is a side-effect of the JVM. If you have a hello world program that does nothing it is indeed fast.
If you are running Clojure you need to run at least clojure.core. Loading that is handled - surprise - by the JVM. As would be expected, if you run other implementations (like Dalvik), this can change dramatically (for worse, usually).
So it is correct that loading the JVM itself is not what's taking most of the time. But loading take so much time because of the JVM.
Whenever "Clojure startup time" comes up, people are quick to point to one thing: either it's the JVM "bloat," or that it's nothing to do with the JVM and is actually all Clojure.
The question is, could Clojure be rewritten in a way that loads significantly faster with the same behavior on the JVM? I'm not sure.
And especially on big projects, the lack of DCE on the JVM can lead to loading a ton of useless code that significantly impacts startup time outside of the Clojure language.
A slightly simpler explanation: GraalVM solves the startup time but clojure's built-in eval wouldn't work after compilation so you need to reimplement it in a simpler way.
I think it's related to dynamic loading of classes (and since Clojure functions get compiled to classes, this might be the issue), not really evaluation per se. But I haven't really looked at Graal native image for a while, so I'm not sure what's the current status.
I've been looking again at Clojure recently, and it seems to have that feature of lisps that their language feature-set seems to just settle over time; unlike Javascript and even Python, it's pretty much the same as it was a few years ago. Instead, it's implementations like Babashka that are providing most of the innovation.
I think this is a good thing! Though I do worry a bit that Clojure can look more moribund than it is. I see a lot of people exploring the language for the first time saying "this tutorial is from 2013, is it still valid?". The answer is mostly yes, but with some changes in tooling that can be dispiriting for beginners, and may make them think the whole ecosystem has gone rusty.
It’s pretty much by design, as the syntax and standard libraries with lispS are intentionally small. In other languages, you see new language syntax or semantics being added, while in a lisp it’s pretty much unheard of to change the syntax, there’s so little to change.
Macros can do a lot of stuff, and as such libraries themselves can add pretty elaborate functionality.
Having said all this, what I do notice with my experience of Clojure is that there are certain “trends” in how you write code. The language itself may not evolve that much, but the styles people write code in it, and how they solve problems, certainly does.
I've been using Clojure in isolation and I have "style anxiety". Can you elaborate (or point me somewhere) on the trends and changes in style you've observed? Thanks!
I think this tends to happen to all lisps, at one point or another. Many new language features in lisp languages don't have to be added to the compiler itself and can instead be distributed as libraries. Compared to JavaScript and others, where if you want something new, it has to go through the compiler/VM/interpreter.
It is built up from SCI (small Clojure interpreter). Which is a Clojure interpreter written in Clojure. That's super cool!
Sci is awesome, and it allows you to create custom Clojure interpreters with whatever subset of Clojure + any additional functions and macros of your liking.
Seeing this makes me wonder if a concatenative/stack (factor?) variant wouldn't be even better. For small script .. when you need larger you go full lisp.
ps: just installed it on my old core i3.. and omg, this loads faster than sbcl.
BB is really cool, I just used it to run an ETL-type process I wrote, and I didn't had to change the Clojure program at all. Also, the built-in batteries were just what I was using anyway (JSON parsing most importantly).
That is not the conclusion at all. Literally the conclusion:
> It’s nice to have another scripting tool in my toolbox: Babashka. Time will tell if I start using Clojure instead of Python as my preferred scripting language, thanks to Babashka. At least in this exercise Babashka did really well.
Actually, „because he likes the language“ is a good enough reason. At least for someone with a hacker spirit. Liking a languange will motivate me more to look into all corners of that language and put my heart in exploring it instead of knowing as much as I need to get the job done.
It's not a "first" language like JS, Python, Java, C#, Ruby etc. so the community appears to benefit from a higher than average level of skill. In the same way that i imagine Erlang or Elixir might enjoy the benefit of more capable than average users.
The fact that entire paradigms can be implemented as libraries is attractive to me - it suggests to me that the Python 3000 or C++11 problems are unlikely to affect Clojure.
For example, Go is a language I find interesting. The whole CSP concept makes a ton of sense to me in writing enterprise software - mainly from a "keeping the architecture cheap to own and change over time" point of view. I appreciate this isn't where CSP was targeted but it lends itself very well IMO to solving this problem because you could decouple components and grow them independently so effectively.
Go is an entire language but core.async is just a library in Clojure, they didn't have to change the language to implement this which is mind blowing to me.
Another example is types. Only some of the code I write actually benefits from types AND most type systems in "enterprise" languages are pretty anaemic. Typing is also really hard to do well, like really really hard. You had a problem, you adopted typing and now you have two problems is generally my thinking here.
Scala was to be my saviour here but that missed the mark quite spectacularly and the compiler is still ridiculously slow today but then along came TypeScript which is pretty darned awesome IMO.
Thanks to TypeScript and modern Python i had learned that i wanted "optional typing" and i was undecided about type erasure at runtime - i don't immediately see a problem with it, i've written probably thousands of lines of Java with generics then used reflection at runtime without losing sleep, but it fills an awkward space in my head - to invest in types but only benefit at compile time seems like a miss.
In Clojure i can follow the TypeScript model but actually take it further to runtime.
This post is too long already. Long way to say Clojure appears very interesting.