Why did Clojure gain so much popularity?
quora.com
quora.com
Less state or immutability means concurrency is easy. Easy concurrency means we can scale[2] applications with ease. I mean ease of development. It takes less work, less mental effort to take something that works for a single thread and make it work in a multithreaded and/or distributed manner over hundreds of thousands of cores. Debugging is easy too. P1 incidents for a microservices written in Clojure takes less time to debug compared to those written in other languages. So no wonder most of our mission critical services are written in Clojure.
For a complex environment where thousands of microservices are deployed on hundreds of thousands of computes, Clojure has been super successful.[3]
[1]: https://github.com/walmartlabs?language=clojure
[2]: http://blog.cognitect.com/blog/2015/6/30/walmart-runs-clojur...
I don't even try to think about concurrent designs too much at work and sometimes feel guilty about it. Occasionally I'll use or suggest someone use a CompleteableFuture, but there is so much code and irrelevant detail compared to Clojure's "wrap body in (future), done". On the other hand some more junior devs have expressed lack of interest in studying concurrency at all since "we don't really work on concurrent or distributed systems", at which point I have to point out that as a company we serve a giant web application with API entry points from multiple bits of hardware with clients all over making concurrent requests... The road is long.
That said, Clojure has to be seen as the sum of its parts. It's the coming together of Lisp, FP and the JVM which truly gives it its strengths.
We use Clojure at my work as well, and ya, everything else you said afterwards is true as well. Clojure is a great productive language. It can scale, is very comparable to Java in performance, simple to maintain, has a complete ecosystem, is batteries included, etc.
But most convincing! It's just so much fun!!
Being fun is an undervalued quality of a language in my opinion. That joy you get from using it actually translates in the quality of the work you do. So it's not an intractable thing, the fun is good for you and for the product!
Day job is great but spending more than half of programming time modernising some of the worst PHP I've ever seen gets old after a while.
Kotlin is actually a pleasure to use so I look forwards to using it.
Yes, in certain environments Java is king and a ton of people use it, it's battle-proven, etc. But so are other stacks.
Clojure, as a programming language, sparks a lot of joy and is a lot of fun to use and reason about. Yet in reality, when working in a production environment, you're basically fighting the always-memory-hungry JVM.
Just give me Clojure running on BEAM and I'll take it over anything in this world.
1. clojerl language: https://github.com/clojerl/clojerl
2. kapok language: https://github.com/kapok-lang/kapok
Not really. Few if any platforms compete with the JVM on performance and observability (monitoring/management) and ecosystem. It's technologically head and shoulders above pretty much all other runtimes (e.g. BEAM has excellent observability, but loses big time on performance and ecosystem). Also, if you're OK with the JVM running at BEAM speeds, you can give it far less memory.
I'm curious about this. How do you do it in practice? Doesn't it just crash with OutOfMemory errors when it runs out of memory?
I've been running a simple JVM web app on my own personal server with an allocated 128 MB of RAM and a lot of that is actually unused. You can do a lot with a heap of just 64 MB.
People aren't aware, but when running your average PHP app you need more than that with just a couple of requests done at the same time, whereas my JVM server can easily handle 1000 requests per second. Similarly Ruby or Python apps can be big resource hogs, especially when you end up setting up multiple parallel processes.
I was also surprised to find that compilation to "native" isn't necessarily efficient in terms of used memory. I've built a similar app in Haskell and the used memory ended up being higher and more unpredictable. If something like Go fares better, it's not because it is compiled to native.
All in all the JVM is actually quite efficient in terms of used memory, for a garbage collected language ;-)
This is untrue. The default configuration is just to do it very reluctantly, but this can be changed.
The JVM and .NET are presently about the same capability-wise, but Java is (or at least, is perceived as) much easier and cheaper to freely experiment with. Unless you need to use C# for some deep Windows integration, this makes Java the obvious choice for anyone without sunk costs in the .NET ecosystem.
It's an official dialect: https://clojure.org/about/clojureclr
Isn't it more or less what Elixir is?
Here's the view of someone who went from Erlang to Elixir: https://www.theerlangelist.com/article/why_elixir
But it's still definitely niche and hardly used anywhere.
re: ABCL: this is really a great project. I don't use ABCL very often but when I need it I am grateful to the developers.
Protocols, records, structmaps, chunked seqs, transients, tagged literals, unchecked arithmetics, primitive arrays, custom data readers, transducers, validators and watch functions for vars and atoms, hierarchies, sorted maps and sets.
Clojurescript was much easier to make because it was written in Clojure, not Java. IMO if we ever see Clojure that is hosted on other runtimes it will be written in Clojure and probably still will have to depend on JVM.
Groovy, Scala and Clojure were the early contenders that people were exploring and Clojure seems to have won a lot of mindshare.
The thing about Java isn't the language or the JVM it's the App Servers. Enterprises have invested heavily in the App Server infrastructure for deploying JVM based code from monitoring, training, etc.
Throwing all that away to use a non-JVM language is a REALLY hard sell. Those app servers are very well done.
Running on the JVM made using Clojure, Scala and Groovy possible in places where other languages wouldn't have been. In the early 2000s, it was Java and everything else.
The .NET ecosystem was awful at that time. The programming world was essentially, Java, Perl and PHP 4 in terms of enterprise usage. People said the words "Enterprise Java Beans" without a chuckle.
People were HUNGRY for better options.
Now you've got a solid C# ecosystem, Elixir, Go, a dominant Python ecosystem, significantly more polished PHP, K8's & Docker that make automating the infrastructure around all different languages consistent...it's just a different world.
I love Clojure and hope it gains more mindshare, but I don't think this is accurate. Scala currently enjoys quite a lot more industry use than Clojure does, for instance there are currently roughly 15x as many postings mentioning Scala as there are Clojure (source: Linkedin.ca).
https://snyk.io/blog/jvm-ecosystem-report-2018
https://www.jetbrains.com/research/devecosystem-2018
It's entirely possible that companies tend to hire for Scala via Linkedin a lot more than for Clojure.
It still kind of is in a lot of places. My country barely bothers to even teach anything else at a lot of schools, and enterprise is almost entirely dominated by the JVM.
You are exactly right. Having worked in Clojure and talked to a lot of other devs here and across Europe, most everyone got into Clojure or Scala because management and cultural complaints left them stuck with the JVM, and at least it beat building more EnterpriseWidgetFactoryGeneratorPatterns.
Oracle and Java are the new IBM, and just like IBM, no one's really happy with them but management yells if you use anything else.
This is true, but...
> Groovy, Scala and Clojure were the early contenders that people were exploring
... Jython and BeanShell were much earlier than those three.
Jython, JRuby, Rhino, and Clojure are JVM languages that are syntactic copies of existing non-JVM languages, and it seems JVM developers didn't want these. Clojure has won within that group.
But the other group is what developers really wanted, i.e. languages with a Java-like syntax but with extra features added. Beanshell came first with dynamic typing. Then Apache Groovy cloned Beanshell and added closures, and Beanshell never kept up. Scala had all that but also had inferred static typing, which Groovy later added but was too late to the game to have much impact. Kotlin then came and merged the best features of Scala and Groovy, and became successful on Android.
I know the problem, and I've danced around this same issue in things like Haskell with strictness annotations, and I've run into the same problem in R, but I'd like to just be able to read and parse a ~1GB CSV file without blowing out RAM with a giant list of thunks.
This was many years ago now, so maybe they've fixed it, but I'm tired of fighting that particular battle.
There's now fully lazy as well as fully strict variants of all sequence functions.
What you want to look into are transducers: https://clojure.org/reference/transducers
Also, the lazy operations were made batching. So they are better optimized now in memory and performance.
IMHO, Clojure's use of the JVM was something of a Faustian bargain. (But net worthwhile).
Reasonable people might disagree, but there is a huge amount of value in having access to a good JIT, GC, and standard library - not to mention the ecosystem around the JVM. Keeping in mind that Clojure started out as a one-person, self-funded project, it was a very reasonable thing to borrow all of this from an existing well-established language. Maybe these things could've been reproduced, but not in the approx. two years that were initially available, and it's not at all clear that building a JIT, GC, etc. would have been the best road to making a useful impact.
If we just look at developments of the last 5 years, and in particular serverless, it's been about taking advantage of those short startup times to use in a server context. When you take that into account, these languages become even more in the lead.
I think the next lisp that has a chance to really take of will be one built on wasm.
MRI ruby had always had a fast startup time and was not originally created for the web... it’s more like a “better” Perl cum toolkit for C/unixy stuff- ironically what python became more popular for despite python not originally being written for that purpose.
Also in the old days of the web.. CGI was literally spawning a process per request.
Regardless it’s bizarre to think nodejs pioneered startup time... Ruby, lua, python, and good lord Perl would really like to have a word with you for starters.
Java was really more the exception.
And I’m not sure it’s true that these serverless platforms are spawning a new process per request ... but wow what is old is new again. Sounds like you’re describing inetd (look it up). Nothing of what you’re talking about is 5 years. Try 30-40.
The 2010s the trends changed towards languages that specialized in faster startup times. That led to serverless.
No one said that these are new inventions. Like all trends its cyclical. I'm just pointing out the trends in order to better understand where Clojure fits in.
Like I said the Java (and .NET) were the exception. As far as trends... java and .net dominate enterprise and they still do. The trend is very slow in the grand scheme.
Frankly your comment seems to be conflating language issues with framework issues with “serverless” and even scripting. I don’t think there is a cogent argument to be had about overall trends with all that. Yes lambda and serverless are a thing but half the whole world still runs on JavaEE or ASP behemoths
Immutability and dynamic typing make it perfectly suited for BEAM.
BEAM is also more suited for Clojure. Less powerful CSP and awkward component would be replaced by the actor and supervision tree.
It's really easy to introduce Clojure into a Java shop, because if things go south, you can throw away a bit of code and replace it with Java. Heck, people on other teams don't even have to know this started as Clojure code.
Asking people to learn both a new way of developing and a new VM? Too much all at once, which is why Clojure spread as far as quickly as it did.
With Clojure being a hosted language, there's nothing stopping it from adopting a new platform or expanding to other platforms or languages. It has actually already done this with Clojurescript (run in the browser or Node.js)[1] and Clojure CLR (run on Microsoft's .NET)[2].
[1] https://clojurescript.org/ [2] https://clojure.org/about/clojureclr
That already exists: http://try.clojerl.online
It's a community maintained dialect. Its maintainer, Juan Facorro is very active. You can see him giving a talk about it here: https://youtu.be/Ow8o9Mm_N7M
I'm sure all the project is in need of are more adopters to really get it off the ground. Check it out!
ZGC from Oracle -- but open source
Shenandoah from Red Hat -- also open source
I know of no other managed runtime platform with the GC options and tunability as JVM.
Also JVM has not one, but TWO JIT compilers, C1 and C2. Your JVM byte code starts out interpreted. It is constantly dynamically performance profiled. If it is getting above average CPU use then it is quickly compiled by C1 into native code, and scheduled to be recompiled soon later by C2. When C2 comes along, it takes its time recompiling your code into highly optimized native code.
Suppose that YOUR function A calls MY function B. And C2 aggressively inlined my B function into your A function. Now suppose my code is dynamically reloaded within the running environment. Now your function has a stale version of my code inlined within. JVM will instantly de-optimize your code back to JVM byte code interpretation so that soon, if it is still a cpu hot spot, it will get recompiled again by C1 and C2.
Let me know of any other runtime platform that even comes close to the things JVM does.
Some of the more advanced Lisps would qualify.
[1]: https://twitter.com/ChrisGSeaton/status/619885182104043520
[2]: http://cr.openjdk.java.net/~pliden/slides/ZGC-Jfokus-2019.pd...
[3]: https://craftingjava.com/using-java-flight-recorder-with-ope...
But it's not just benchmarks. It's capabilities.
He should please provide references that compare other runtime platforms' capabilities side by side with JVM's capabilities.
How many weeks work do you imagine that would be?
While no language / platform / os / etc is perfect, JVM as a managed runtime platform is hard to beat. More than two decades of research in its JIT compilers, and multiple GC implementations with various knobs and dials for tuning and monitoring. Battle tested.
Someone mentioned SBCL. While I am a fan of Common Lisp, and generally all Lisps, I would cringe to see SBCL with terabytes of heap and hundreds of CPU cores running a serious workload -- and see how well it holds up.
Just an example: You can Google for this, but in 2012 Twitter switched from Ruby on Rails to Java. They have YouTube videos explaining their change over. Basic reasons: performance and scalability. They have to handle BILLIONS of tweets per day and route each one to multiple places with notifications in near real time.
That's nothing SBCL can do, but one can save an executable and start that in a subsecond without the need for a terabyte of RAM.
Generally there is a lot of stuff in the JVM which makes it less attractive as runtime for Lisp: more complex code loading via 'class loaders', lack of support for memory representation of Lisp's data types (like CLOS objects, which are more dynamic than Java classes), lack of TCO, lack of easy AOT compilation, lack of easy image dumps, lack of resumeable exceptions, ...
And in 2019 something like Eclipse still is a bit clunky to run on my 8 core Xeon - part of the reason could be a less than great GUI implementation. In IntelliJ I need to restart my IDE for every simple plugin... probably features like live-updating haven't made it yet into popular apps.
A few tens of magabytes maybe and the JVM runs a complete Hello World in about 60ms, but yeah, Clojure does a lot of work on startup. It's more of a Clojure issue than a JVM issue, though. It's true that HotSpot is mostly designed for long-running applications. Other JVMs -- like Substrate VM (AKA Graal native image, which isn't quite a JVM yet) and Excelsior JET give you AOT compilation (no warmup).
> makes it less attractive as runtime for Lisp
I think Clojurists (which probably outnumber all other professional Lispers combined several times over) would disagree. I'm sure there are use cases where you'd like to use Lisp and HotSpot is not the right choice, but it seems like those cases are vastly outnumbered by the cases where it is. Domains where HotSpot is certainly not the right choice are usually either client-side web or domains where C/C++ dominate.
> lack of TCO, lack of resumeable exceptions
Delimited continuations and eventually tail calls are coming as part of Project Loom (https://wiki.openjdk.java.net/display/loom/).
> lack of easy image dumps
HotSpot's monitoring, management and observability are almost unmatched (maybe Smalltalk is somewhat better in some regards, and BEAM in some others). Whatever you may be lacking on that front is probably offset by something probably even more valuable (these capabilities have been designed over the past decades to suit the needs of millions of developers running huge backbones).
I'm not saying that HotSpot is always the best tool for the job, but it's the best tool for the job for a huge portion of the software industry.
> And in 2019 something like Eclipse still is a bit clunky to run on my 8 core Xeon - part of the reason could be a less than great GUI implementation. In IntelliJ I need to restart my IDE for every simple plugin... probably features like live-updating haven't made it yet into popular apps.
You don't need to use a Java IDE if you want to write Clojure, although the Java IDEs are also pretty unmatched -- a matter of getting used to.
So I need to switch to exotic JVMs to get better performance?
> I think Clojurists (which probably outnumber all other professional Lispers combined several times over) would disagree
Does the number matter, since most of them have a) never used Lisp nor b) do they know anything about Lisp implementation? Things like 'no TCO' in the JVM are no question of the number of people using it, it's simply a fact.
If the JVM would be a better match, then Lisp implementations on it would have a better start-up time - like most Lisp runtimes have.
There are many reasons to use the JVM, but good support in the JVM for Lisp implementations is not one of the strong points.
> Delimited continuations and eventually tail calls are coming as part of Project Loom
Which means, after reading the linked doc, that neither resumeable exceptions nor TCO are in JVM, neither currently nor in the near future. Now the usual arguments about TCO are: a) one does not need TCO b) it makes tracing difficult c) it's a security problem d) we can do recursive self-calls e) a future version of the JVM will provide annotatations for tail calls f) using TCO would provide interop problems with JVM code... etc etc.
The fact remains: TCO is not supported by the JVM, while this capability is a part of many other runtimes.
> probably even more valuable (these capabilities have been designed over the past decades to suit the needs of millions of developers running huge backbones).
That's all great, but I don't develop huge backbones all the time.
> I'm not saying that HotSpot is always the best tool for the job
Your original claim was this: 'if someone is going to assert that another platform is competitive with JVM, then not only is performance important, but capabilities as well.'
We are now talking about capabilities like full TCO and the JVM simply does not provide it. It currently provides no TCO at all.
> You don't need to use a Java IDE
It's not about what I need, it's that capabilities like extending the IDE on the fly are still not available in an IDE like IntelliJ - why? After downloading a plugin, it wants to restart the IDE. Something which a typical Lisp system on a Lisp runtime brings for free, because of its capabilities.
Depends what you mean by "better". HotSpot is optimized for peak performance (which is higher than that of other Lisp runtimes), and to suit the needs of server-side development. Other VMs target other uses -- fast startup, embedded etc.
> If the JVM would be a better match, then Lisp implementations on it would have a better start-up time - like most Lisp runtimes have.
It's already a better match, because that's what most Lispers prefer. If you mean it could be even better than it is, then I agree. I also agree that it's not the best match for, say, Racket. Now, it's perfectly fine if you prefer Racket over Clojure, but most people prefer Clojure to Racket, partly because overall the runtime gives them more of what's most important to them. Would it be cool if the JVM had more features that would allow it to be a better match for Racket? Sure.
> Does the number matter
Yes, because Clojure is the most popular Lisp, the features it has are probably the ones most important to Lispers.
> Things like 'no TCO' in the JVM are no question of the number of people using it, it's simply a fact.
Yes, but how much not having TCO matters can be answered by popularity. That people use Clojure much more than they use Lisps on runtimes with TCO suggests that other things matter to them more.
> neither currently nor in the near future
Currently, no. Near future -- probably. I'm the technical lead for that project, and it's very likely that delimited continuations would arrive soon (in fact, you can already build the prototype today, and early access builds will be available in a couple of months).
> Now the usual arguments about TCO are ...
Well, as it's being addressed only now rather than, say, 10 years ago means that it's not a top priority, but judged to justify some reasonable effort at this point in time.
> TCO is not supported by the JVM, while this capability is a part of many other runtimes.
True, but the fact remains that the totality of capabilities of the JVM is considered more attractive by more people than that of most other runtimes.
> That's all great, but I don't develop huge backbones all the time.
I am not saying that you, personally, should definitely use the JVM, only that it seems that the JVM is a great platform for many languages, including Lisps, and that the use cases it targets are very desired.
> Your original claim was this ...
I believe I also said that I mean capabilities for the domain HotSpot targets. Obviously, other things may be more attractive for, say, embedded software.
> It currently provides no TCO at all.
There are other things it doesn't provide. But by comparing capabilities I did not mean one by one, but the totality of those of one runtime vs. those of the others.
> It's not about what I need, it's that capabilities like extending the IDE on the fly are still not available in an IDE like IntelliJ - why? After downloading a plugin, it wants to restart the IDE. Something which a typical Lisp system on a Lisp runtime brings for free, because of its capabilities.
No, that capability is definitely there. I'm guessing the IDE authors just didn't think it's worthwhile to use. NetBeans, for example, doesn't require a restart after downloading plugins (or, rather, rarely does).
What relationship have continuations to making Java exceptions resumeable?
> But by comparing capabilities I did not mean one by one, but the totality of those of one runtime vs. those of the others.
Not sure how you compare a totality, other than actually having a list and compare them one by one.
My feeling: you make a claim, but you have never used one of the Lisp runtimes like Allegro CL and you simply don't know what they provide and when it comes to specific capabilities, they are not important to YOU, because JVM users don't use them - given that they have no choice.
The JVM isn't the predominant enterprise platform because it is the best Lisp platform. Very few people in companies care whether it runs Lisp well or not. People have chosen it as their platform for their niche language, because of its domination in the specific enterprise market - not because it is technically or capability-wise a good platform for their niche language. Actually even though the JVM now exists for a long time, none of the dynamic languages have their best / most performant / dominant implementations on the JVM. Not Smalltalk, not Lisp, not Scheme, not Python, not Ruby, ...
We don't want resumable exceptions for Java. But if you want to implement a Lisp that has them on the JVM, you'll be able to use continuations for that.
> but you have never used one of the Lisp runtimes like Allegro CL and you simply don't know what they provide and when it comes to specific capabilities, they are not important to YOU, because JVM users don't use them - given that they have no choice.
I have never used Allegro, and, as I said, some capabilities that are important to some people may be missing on the JVM. But it seems that most Lispers prefer what the JVM has to offer, which is high performance, low-latency GC, low-overhead monitoring, and a huge ecosystem.
> not because it is technically or capability-wise a good platform for their niche language.
True, but most people don't care about what's good for their language, they care about what's good for their program (and users). It seems like more people prefer Lisp on the JVM, even if they have to give up some features (and they'll need to give up fewer features with Loom).
> Not Smalltalk, not Lisp, not Scheme, not Python, not Ruby, ...
Python and Ruby are actually most performant on the JVM nowadays. Look up TruffleRuby and GraalPython. They're just very new. Even TruffleJS is almost as good as V8.
So the Java libs I'd have to use would not support them. Not so great.
> I have never used Allegro
So the totality of your opinion of the JVM being better for Lisp comes from exactly what knowledge? Oracle marketing?
> But it seems that most Lispers prefer what the JVM has to offer, which is high performance, low-latency GC, low-overhead monitoring, and a huge ecosystem.
You mean Clojure users recruited from Java? I have never seen many Scheme, Common Lisp, Javascript, Julia, ... users on the JVM.
> Python and Ruby are actually most performant on the JVM nowadays. Look up TruffleRuby and GraalPython. They're just very new. Even TruffleJS is almost as good as V8.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Really? Looks to me that it's not particular fast.
[GraalVM CE, Java 1.8.0_202] on darwin
Type "help", "copyright", "credits" or "license" for more information.
Please note: This Python implementation is in the very early stages,
and can run little more than basic benchmarks at this point.
Haha, basic benchmarks, okay. Size '178939568' okay.Probably the largest Python implementation in the world...
Well, resumable exceptions are not so great... We really don't want them in Java (remember that Java is a language originally designed by Lispers).
> So the totality of your opinion of the JVM being better for Lisp comes from exactly what knowledge? Oracle marketing?
I've used Racket (and PLT Scheme before it) quite a bit (I was a Schemer when Java barely even existed), and Guile, and, of course, Clojure. Also, Oracle marketing is quite terrible.
> You mean Clojure users recruited from Java? I have never seen many Scheme, Common Lisp, Javascript, Julia, ... users on the JVM.
I started using Scheme in the mid-nineties. Unfortunately, I haven't seen many Scheme and Common Lisp users anywhere. Clojure is the first in over three decades to be used on any noticeable scale in industry. There are plenty of people that run JS on the JVM (probably ten times all non-Clojure Lispers), and Julia is still new.
> Really? Looks to me that it's not particular fast.
It's very fast. Here's some benchmarks with early prototypes: http://thezhangwei.com/documents/oopsla113-zhang.pdf
Beats PyPy.
I thought they are?
> We really don't want them in Java (remember that Java is a language originally designed by Lispers).
Who of the Oak team were Lispers? Not even Gosling has ever seen or used a competent Lisp implementation - he wrote a 'Mock' Lisp without lists ;-) for an Emacs written in C on Unix. One of his many projects. Stallman later implemented a slightly better Lisp using Goslings C code as a start.
> I've used Racket (and PLT Scheme before it) quite a bit (I was a Schemer when Java barely even existed), and Guile
None of them have especially interesting runtimes. Racket now gets a new runtime based on Chez Scheme from Cisco. Chez Scheme is actually great. You should have studied that. Well, source code is now available from Cisco. It has a nice AOT native compiler.
> I started using Scheme in the mid-nineties. Unfortunately, I haven't seen many Scheme and Common Lisp users anywhere
You were too late. SUN had its own Lisp offerings in the 80s. Like DEC/DIGITAL, IBM, Apple, HP, Apollo, Texas Instruments, Xerox, ...
Java outperforms SBCL by a very wide margin -- 1.25-4x faster -- except in the short-running benchmarks (and except the spectral-norm test, where SBCL is 5% faster), as the game does not perform warmup, and includes Java's compilation time (you can see that no Java result is under 2 seconds).
> you can see that no Java result is under 2 seconds
That's because the workloads were chosen to push Java results above a couple of seconds.
> as the game does not perform warmup, and includes Java's compilation time
How many 1/10ths of a second do you imagine that takes?
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
I have looked at some example programs in the shootout game where different implementations use completely different low-level algorithms, including code that's very unidiomatic, so those benchmarks are of low quality. And yes, Java does have a disadvantage here (I work on OpenJDK, BTW, so I don't need to imagine).
Anyway, I’m not sure which side you’re arguing, because the benchmark game did show a rather drastic win to Java (TBH, I would have been surprised if that were not the case). I simply qualified that win because the shootout game is not a high quality benchmark.
Which programs?
> including code that's very unidiomatic
Which programs? (And don't you expect code written to be fast to be different?)
> I work on OpenJDK, BTW, so I don't need to imagine
So why haven't you told us how few 1/10ths of a second?
> I’m not sure which side you’re arguing
I don't care one way or the other, I'd just like your comment to do better than name-calling.
All these things make the inter-language comparison as presented nearly useless. Crucially, the site itself doesn't even state what it is that it purports to measure or compare, like a paper without any hypotheses, claims or exposition. That alone is enough to place it in the low-quality benchmark category (regardless of the merits of the problems themselves, which I haven't looked at). But that's where most non-professional benchamrks are. ¯\_(ツ)_/¯
Now, I would guess that there is some meta-hypothesis to the game pertaining to markets, but that's not what's being analyzed or even presented.
> Which programs?
I tried to look at programs that report big performace differences and I'm looking at C++'s regex-redux, and I see it uses a library that isn't in the standard library. Or look even at "Java" vs "Substrate VM" pidigits. These are two VMs for the same language, yet the programs are completely different. I'm not sure what I could learn from that about HotSpot vs Substrate VM.
Or, the example that caught my eye when I reached the game when looking for SBCL benchmarks -- SBCL's reverse-complement, that is reported to outperform C by more than 10x. Well, not only does it use a completely different low level algorithm (doesn't use parallelism), it doesn't outperform C or Java at all, but crashes, and what appears to be some junk result is reported. Had it not crashed, it would still have used a very different algorithm.
> So why haven't you told us how few 1/10ths of a second?
Because that would be misleading. Now, I admit that what threw me off at first was Java's performance compared to SBCL's reverse-complement, but that's just a mistake and has nothing to do with compilation. Real programs can take many seconds to compile, but looking at the benchmarks, they do look quite small, so you are correct that they will be compiled rather quickly (less than a second). However, I see that Java is run with tiered compilation, and obtaining good optimization, even for such small programs, can still take many seconds (say 10 or 30). As a general rule of thumb, we always warmup for at least a few seconds, even for microbenchmarks.
It is true that the difference would often not be large compared to the inter-language differences reported by the benchmark -- and as I said above, it's unclear what it is they compare -- but they can certainly be in the 5-10% range, and maybe more. This is a big difference for what I call "high quality" benchmarks, benchmarks intended to really give a precise performance measurement. For example, Twitter has a whole machine learning system that tunes and re-tunes their JVMs just so they can get a 7% performance improvement. Even the game's own examples that you linked to and seem to say, see, warmup doesn't matter, report differences of 10-200%.
> Which programs?
Now I'm looking at Haskell's fannkuch-redux
> And don't you expect code written to be fast to be different?
Yes, and if the comparison was for the same language (and compiler) then this would be interesting, but what does this tell you when comparing different languages?
Browsing some more I see that the binary-trees programs for Java and Substrate -- again, same language -- use completely different algorithms. Not to mention that just the "Java" programs alone use such a weird range of parallelism constructs, that I have no idea whether there was any reason behind it, the programmers simply didn't know about proper parallelism constructs, or the program is so old that it was written before newer constructs were introduced.
The program with the fastest measurements on OpenJDK did not have the fastest measurements on Substrate VM.
Someone with sufficient curiousiy might choose to investigate — Why?
Does it? How do you know that the same programs were run?
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Is that what the presentation emphasizes most?
"Will your toy benchmark program be faster if you write it in a different programming language? It depends how you write it!"
"Which programs are faster?"
"Which programs are fast?"
"Non-motivation: We are profoundly uninterested in claims that these measurements, of a few tiny programs, somehow define the relative performance of programming languages."
2) Or look even at "Java" vs "Substrate VM" pidigits.
Currently 2 Java pidigits programs are shown.
The program with the fastest measurements on OpenJDK uses GMP and something different needs to be done to make that program work on Substrate VM.
3) …but crashes, and what appears to be some junk result is reported.
What is reported? Measurements at the largest workload both programs worked.
4) examples … you… seem to say, see, warmup doesn't matter
See, for programs with runtimes of 0.24s, 1/10th of a second is a lot!
See, for programs with runtimes of 4s and 20s, 1/10ths of a second… !
5) Now I'm looking at Haskell's fannkuch-redux
And… ?
6) … if the comparison was for the same language (and compiler) then this would be interesting, but what does this tell you when comparing different languages?
That seems to be a subjective statement about your interests.
Someone else might think that neither "tell you" much until you investigate Why?
Yes, because that's what the site shows most prominently: comparisons between languages.
The fact that the explanations to the benchmarks basically say that these comparisons mean nothing doesn't make the comparisons mean much more than nothing, especially as there's no explanation to what the comparisons do (or are supposed to) mean. So in addition to having no hypothesis, no analysis and no exposition, there's a notice saying, don't use these comparisons for so and so. Then what are those comparisons for?
Again, I don't think the game's comparisons are worse than many other benchmark comparisons out there, but that doesn't make it good. What's unfortunate is that unlike those other benchmarks, they have a lot of data that could be used for some really cool stuff (not to compare languages).
It's possible that the language competition is meant to encourage more people to submit and show how their favorite language wins, but there's no information about the market aspects.
> See, for programs with runtimes of 4s and 20s, 1/10ths of a second… !
I run a toy benchmark that's the same size as the game's, and HotSpot's C2 gives me a 30-50% boost after ~10s. It really varies by program (not by problem). This tends to be worse for programs that use a lot of parallelism and aren't configured to run compilation in blocking mode, because then the application threads compete with the compilation threads, but again, there isn't really a good rule of thumb here. In any event, it is simply wrong that C2 would never yield significant performance boosts after a few tenths of a second, even on small programs (it may be true for some specific benchmark entries and maybe even all current ones, it may vary by JDK version, and it definitely varies on whether HotSpot uses Graal or C2). Although, given all the other serious problems that render the comparisons mostly meaningless (different algorithms), my guess would be that this effect is smaller by comparison.
Still, there's a lot of cool stuff the game could do with the current programs (and historical ones), but comparing languages is not really one of them.
Program after Program after Program.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Of course, it's really not possible to show any computer program which is not written in a programming language.
> … basically say that these comparisons mean nothing …
Obviously not: please don't put your words in others' mouths.
> … a lot of data that could be used for some really cool stuff…
Take the data and do that really cool stuff.
https://salsa.debian.org/benchmarksgame-team/benchmarksgame
> … but there's no information about the market aspects.
What do you mean by "market aspects" ?
> …it may be true for some specific benchmark entries and maybe even all current ones…
So it would be wrong to try to dismiss those measurements, based on nothing more than extrapolation about how some other toy benchmark program might perform.
> … cool stuff the game could do …
You're allowed your own personal opinion about what's cool and what isn't (and no doubt others will have their own personal opinion).
It's certainly not obvious to me what they comparisons mean (is it obvious to you?) and the authors of the site don't even state, let alone support, a hypothesis. They just say what the comparisons don't mean. So unless it's a work of art, I think observers can be forgiven for thinking it doesn't mean anything.
> What do you mean by "market aspects"?
I mean a hypothesis about "free market competition" driving people to write really fast programs.
> You're allowed your own personal opinion about what's cool and what isn't
True, but I meant something that could be used in a more rigorous/meaningful way (e.g. comparisons where only one variable changes). Meaning is perhaps also subjective, but I don't even know the subjective opinion of the meaning behind the game, because the site doesn't say.
> https://salsa.debian.org/benchmarksgame-team/benchmarksgame
Cool.
Anyway, now that I've taken a slightly closer look, I realize I spoke too soon when I said that the SBCL vs. Java benchmark is "better than nothing." If, say, Java uses parallelism while SBCL doesn't, then if Java is 4x faster that may not be a "better than nothing" indication.
Program X measured less than Program Y.
> … comparisons where only one variable changes…
Seems like your idea of a comparison, between for-example Lisp and Java programs, would require writing all the Lisp programs as if they were being written in Java. (We used up our "one variable change" with SBCL instead-of OpenJDK).
> I spoke too soon
You chose to summarize data: "Java outperforms SBCL by a very wide margin -- 1.25-4x faster".
Do you mean that you made mistakes when you made your summary.
That's like asking what does a program do and being told, "it does what the code says." Technically true, but not what we mean when we ask. Also, I don't think that's really what the site's authors thought. After all, when I click on the Haskell page, I don't see a comparison of Haskell programs, but a comparison of the fastest Haskell program with the fastest Java program. It's not obvious to me what that means, or even that it means anything at all.
> Seems like your idea of a comparison, between for-example Lisp and Java programs, would require writing all the Lisp programs as if they were being written in Java. (We used up our "one variable change" with SBCL instead-of OpenJDK).
That's exactly right, but because that's rather tedious, I would present just meaningful comparisons rather than non-meaningful ones, so no inter-language comparisons. I'm not saying they're always meaningless, but they can't be made with the data currently available on that site. Unfortunately, we cannot manufacture (non-fictitious) meaning out of data as we like.
> Do you mean that you made mistakes when you made your summary.
Yes. By "better than nothing" I overestimated the significance of results, at least in this case. After a closer look I realized that different programs use different algorithms, and so the comparison tells me nothing about the relative speed of those languages.
No it is not. Program X may not have measured less than Program Y.
> … meaningful comparisons…
Earlier you complained there was "code that's very unidiomatic" but now you say "writing all the Lisp programs as if they were being written in Java" would be exactly right?
1) You seem to be contradicting your own argument.
2) Your desire for "rigorous" comparison is in-conflict with your desire for inter-language comparison.
> … different programs use different algorithms…
For some programs, for some definition of "algorithm".
> … the comparison tells me nothing about the relative speed of those languages.
What is the speed of a programming language?
I may have misinterpreted your comment. By "as if they were written in Java" I meant using a similar algorithm. Alternatively, there could be a rule about idiomatic code (enforced by experts), which would compare very different things, but would also be at least somewhat meaningful.
> For some programs
I don't know. Whenever someone refers me to a result on that site that looks weird, I see very different algorithms.
> for some definition of "algorithm".
For a very well-accepted definition of an algorithm in the context of real performance comparisons.
> What is the speed of a programming language?
I'm not sure (although perhaps I could come up with some definition given enough thought), but I didn't make a website that compares languages based on performance. In fact, I said that the website is totally meaningless. But the people behind it may have something in mind, and if they do, they're not telling us. All I can do is think of ways to use the site's data in ways that can at least be meaningful.
Will someone else look at those similar algorithms and "see very different algorithms"?
> … a result on that site that looks weird, I see very different algorithms.
Do regex libraries for different languages in-fact always implement the same algorithms?
> What is the speed of a programming language? I'm not sure…
You did say "Java outperforms SBCL by a very wide margin -- 1.25-4x faster".
Saying "… so the comparison tells me nothing about the relative speed of those languages" suggests you are looking for something that tells us about "the relative speed" of those languages?
I would think so.
> Do regex libraries for different languages in-fact always implement the same algorithms?
I don't know. But I do know that when something is presented as an answer, it's good to know what the question is.
> suggests you are looking for something that tells us about "the relative speed" of those languages?
I can think of a couple of ways the two can compare. One is the relative speeds (and other metrics) of programs of a similar nature, written simple idiomatic code. This gives a general "feel" for what kind of workloads different languages may be suitable for. Another is comparing the performance of the compiler/interpreter on some microbenchmark of the same algorithms, preferably written by people who coordinate, and preferably without mixing too many variables (e.g. measuring parallel scheduling separately and regex separately). Either one of these is of some interest.
The benchmark game does neither. If there is some idea behind the site's comparisons, it's certainly keeping it a mystery.
So when you say "the relative speed of those languages" you actually mean the relative speed of programs?
> … programs of a similar nature … preferably without mixing too many variables…
Seems vague, no-longer rigorous.
We can make mystery for ourselves — no one can force us to understand.
No, I mean the relative speed of programs with some carefully chosen relation between them. For example, there is a big difference between "relative income of individuals" and "relative income of social groups," even though the latter could be reduced to the former. Whether a comparison of individuals is meaningful as a comparison of groups depends on how the individuals are chosen and aggregated. Clearly not every choice of individuals would make for a meaningful comparison of groups and not every choice of programs makes for a meaningful comparison of languages. The choices made by the game do not seem at all meaningful for the comparison of languages, but if there is some rhyme or reason to the choice, the website doesn't say what it is.
> We can make mystery for ourselves — no one can force us to understand.
True, but when a website chooses to present data in some aggregate way that seems to defy any familiar forms of aggregation and reasoning without so much as stating what the meaning of the presentation is, it is itself mysterious. I agree that it is possible that the comparisons are not worthless, but without any statement of a hypothesis, it is the most reasonable conclusion. I am sure that the authors of the website would agree that if they don't even attempt to say what the meaning of the comparisons is, even in their own minds, it is reasonable to assume it means nothing.
> > So when you say "the relative speed of those languages" you actually mean the relative speed of programs?
> No, I mean the relative speed of programs with some carefully chosen relation between them. For example, there is a big difference between "relative income of individuals" and "relative income of social groups," even though the latter could be reduced to the former. Whether a comparison of individuals is meaningful as a comparison of groups depends on how the individuals are chosen and aggregated.
You can choose which program measurements to compare.
> > We can make mystery for ourselves — no one can force us to understand.
> True, but when a website chooses to present data in some aggregate way that seems to defy any familiar forms of aggregation and reasoning without so much as stating what the meaning of the presentation is, it is itself mysterious.
You have not shown that is what that website does.
I agree that it is possible that the comparisons are not worthless, but without any statement of a hypothesis, it is the most reasonable conclusion.
Yet another tendentious assertion!
Program X measured less than Program Y.
I am sure that the authors of the website would agree that if they don't even attempt to say what the meaning of the comparisons is, even in their own minds, it is reasonable to assume it means nothing.
Once more, you are putting your thoughts and words in others' mouths.
Yes, but the website also chooses, and it is the website's choice that I think is bad. Also, it doesn't seem like the game even has comparable programs in some/many/most cases.
> You have not shown that is what that website does.
How can I show what a website doesn't do? If you think they say what the hypothesis is, show me. I haven't been able to find it.
> Program X measured less than Program Y.
First of all, when something means nothing other than itself, we say it has no meaning. If the comparisons on the website are meant to suggest nothing more than just their own definition, then they are meaningless.
Also -- come on. The website presents comparisons for specific choices of X and Y. What is the meaning of the choice and the resulting comparison? Your position that it's not meant to mean anything (other than itself), sounds disingenuous because the website still makes some very specific choices that seem to hint at a meaning (e.g. why not compare the second fastest programs between languages instead of the fastest?) yet the meaning is never explained.
Anyway, we're going in circles.
For someones definition of "comparable".
> How can I show what a website doesn't do?
You can try to show your claim — "… chooses to present data in some aggregate way that seems to defy any familiar forms of aggregation and reasoning…" — has some basis in reality.
> … when something means nothing other than itself, we say it has no meaning.
That may be what you say. Show why we should think that's what people interested in "Why did Clojure gain so much popularity?" would say.
> … why not compare the second fastest programs between languages instead of the fastest?
Inexplicable! Or obvious: there would need to be two programs for each, rather than one.
> Anyway, we're going in circles.
I agree, you repeat your assertions without establishing anything.
As Rich Hickey says: because it has _reach_
The JVM is widely used.
ClojureScript on JS is because JS also has reach.
My self-interested perspective on Clojure today (as an expert in a couple other Lisp niches) is that I'd be happy to learn Clojure if someone would pay me to, but it's not relatively attractive to me now, in the ways that a Lisp is more attractive than a non-Lisp. (Nor in the way that Rust is in some ways more attractive than C and C++. I've been a senior developer in those, and Rust is sufficiently different, and might open some new doors.)
[1] https://insights.stackoverflow.com/survey/2019#technology-_-...
[2] https://insights.stackoverflow.com/survey/2019#developer-pro...
I think Clojure is awesome and have used it for some projects but I also think that causal relationships in the SO survey are often completely misunderstood.
I think in practice most less experienced developers cannot justify investing their time in such a niche programming language as Clojure. It's not like Clojure developers are paid best because they are the most experienced. It's because they could afford the time to master a programming language with very little demand on the job market.
I don’t want to imply that these surveys are rubbish, just that I do not think they give the whole picture
Together, these cover cloud programming, web, embedded, desktop, mobile, OS, networking, games, machine learning, data science and probably quite a few other industries.
The only languages I can think of that see quite a bit of use and that are missing are Lua and TCL; and some of the more obscure ones that would still have been interesting are Ada, some MLs, Haskell, and CommonLisp.
Why do you believe that this list is biased towards web development?
And, out of curiosity, what languages are you working with? By your mention of Emacs, I'm thinking maybe some kind of Lisps?
1) Lexemes have a specific structure.
2) Core data types have a specific syntax (representable with a grammar).
3) The data types used to represent programming constructs also have a specific set of syntactic rules by which they are assembled.
It's a different kind of syntax than the usual - more raw/direct, perhaps - but it's a misconception to say there is an absence of syntax. A big part of the reason I mention this is that the nature of Lisp's syntax is one of its biggest strengths.
It’s similar to learning declensions in Russian/German/Latin: weird and seemingly pointless to English speakers, and then usually fully internalized after a few months, with better intuition for costs and benefits of syntax vs cases.
I’d argue that the Clojure learning curve is significantly shorter than that also.
The best criticisms of Clojure (and I’ve written a lot in it) have nothing to do with parentheses, but rather the difficulties of grokking other people’s dynamic, untyped code.
Some programmers think types obviate the need for docs. Some programmers think concision obviates the need for docs. Except in the most trivial cases, they're both wrong.
[The Pure Function Pipeline Data Flow](https://github.com/linpengcheng/PurefunctionPipelineDataflow)
Exception handling is included in the function of side effects, and the exception is handled as actively as possible. In management, if an exception is encountered, always throw it to the superior, who will be fired:-)
you can do it like ring, treat client and server as db, query each other.
Arithmetic doesn't even look right in programming languages with infix operators. Go to the "Chalk is still one of the primary tools of mathematicians" article here, and try typing the inequality in Java. Mathematicians aren't writing "Math.floor()" and "% 2" on their blackboards.
I think you're optimizing for the wrong thing. Arguing that we need infix operations for 1% of our programs, so that arithmetic looks like how we entered it on a TI-81 or Apple II, is a false economy. It was a good tradeoff 25 or 50 years ago, but the majority of programming has moved past writing programs in terms of arithmetic on registers.
That said, there is a nifty macro [1] which adds infix support to Clojure, if you really want that. It even supports symbols like "√", unlike any other language I can think of!
I've been spoiled by Scala, but certainly most languages allow a "subject.verb(object)" style for general-purpose code, and that reads a lot better (particularly for those looking from the business domain) than "(verb subject object)".
> It even supports symbols like "√", unlike any other language I can think of!
Heh, again I'm spoiled by Scala, where √ is just another method/operator (there's no distinction) name if you want it to be.
Why do you think this?
Programming isn't natural language, and I don't even know what the "subject" or "object" is in most cases.
It's not, but it should be close; readability is important, and particularly when you want to express domain logic it's important to be able to hew closely to the actual language of the domain. Arithmetic is the most obvious example of this but it's just as true for other domains.
_ is genuinely costly syntax, as are by-name parameters (though less costly in an age of IDEs). I consider _ to be worth its while (indeed I miss it a lot when writing lambdas in any other language); if you're going to have syntax in your language at all then it's one of the most general-purpose, effective constructs I've seen.
https://twitter.com/pembleton/status/1116343189437394944
A new line can be significant syntax in Scala!
With s-expressions, all of this mental overhead goes out of the window. I have regular boring syntax where pretty much everything works the same way. It's incredibly liberating. On top of that, s-expression syntax facilitates structural editing, where I'm manipulating the code tree semantically as opposed to thinking about moving lines of text around.
It's also pretty easy to add prefix operators in e.g. Haskell or ATS (for something like square root)
Complex syntax introduces a ton of mental overhead, and it's a constant source of weird and hard to debug errors because code often does something subtly different from what you think it's doing. S-expressions enforcing a consistent and explicit syntax frees up a ton of mental space for me.
Our whole stack is Clojure/Clojurescript. Clojure has helped reduce bugs and improve stability across the board.
I enjoy writing it everyday and wish more people would give it a shot.
At the same time Clojure benefits from being a hosted language. We don't have to build everything from scratch, instead we leverage two of the biggest ecosystems and reap all the benefits for free.
It's still a very niche language to be sure, but from what I've seen there has been a lot of commercial adoption in the last couple of years. There are new a few consulting companies, such as JUXT and Metosin, that are focusing exclusively on Clojure consulting. A lot of the libraries in the ecosystem are now maintained by these companies as opposed to individuals. There are also efforts like Clojurists Together for funding Clojure projects sustainably. These efforts would not have been possible without growing commercial use with companies willing to donate money.
So more companies using Clojure directly leads to a better ecosystem, and that in turn makes the language more attractive. The state of tooling and libraries is dramatically better today than it was even a couple of years go. I'm excited to see how the ecosystem grows this year.
One of the projects I'm most excited about is https://cljdoc.org/ which aims to provide a one stop shop for polished Clojure library documentation. I just saw a presentation about it at Clojure North, and I was very impressed with where it's at already.
Btw, thank you for all your open source projects and comments on various GitHub repos.
Lisp is used in AI. https://medium.com/ai-society/the-lisp-approach-to-ai-part-1... https://ai.stackexchange.com/questions/2236/why-is-lisp-such...
Oof. So true, I hate this field. But I guess you could say similar of almost anything. Maybe I just hate humanity.
You left out the immediately following bit, "Experienced software developers don't chase the hype". I cringed. But maybe if my interpretation of the overall point is correct, I can agree with a point that Clojure attracts grumpy programmers who have Seen Things and gives little lip service to beginning devs.
Where I work we have 3-4 different ways of accessing our monolithic backend, and on top of that 3-4 different ways of accessing and manipulating application state.
It drives me up the fucking wall.
I'm not sure I agree with the little lip service statement. I feel like the feelings are most likely mutual.
Stable systems that don't need constant heroism and don't diverge much from original choices made at the architecture level don't make for good places for juniors to make mistakes and learn from them.
Agreed. Data storage and transmission formats have the potential to dramatically outlast the modules where they originate. This is in large part because these things represent commitments to others, where the internal design (or implementation language) of a specific module really doesn't. So, these days I tend to be much more interested in the design at the interface level than anywhere else.
This works both for and against languages like Clojure. It works for it, to the extent that fewer people have to care what you use when you're building the innards of something. Against it to the extent that it can put a potentially relatively low bound on the gain that can be realized through the use of better (?) tooling like Clojure.
(There's also something to be said for the fact that languages like Clojure tend to snowball in my experience better than other languages. A single program written in Clojure is nice, but it's in the ecosystem/network effects where it really pays off... and that takes a lot of time to see. More than most people take.)
It's actually a pretty sizable moat. Python broke encodings between 2 and 3 and split the community for at least a decade. Some companies will likely never be able to migrate from Python 2 to Python 3. Now imagine breaking now `dict` works.
https://bytes.yingw787.com/posts/2019/01/26/concurrency_with...
Although there's Luminus web frameworks are generally frowned upon in Clojure-land which concedes mindshare to other languages which recognise the importance of frameworks and killer apps. With so many languages competing for mindshare it is that much easier today for a language, however original and superior, to be sidelined by paying customers. For developers looking for a language which will also help pay the bills Clojure is unfortunately not an option.
The chickens, experienced Clojure devs, don't need a framework, and are easily able to do whatever they want and use Clojure to pay their bills. Thus they're not motivated to build frameworks.
On the other hand, the eggs, devs unfamiliar to Clojure, find it difficult to get anything done in Clojure, and are looking for frameworks that abstract away some of the expertise needed to manage a full Clojure web app. They're hoping they can start using it to pay their bills before they become an expert in it, through the use of a framework.
I'm not sure how the future will unfold here.
Traditionally, web was all about serving static documents, and server side rendering model is a perfect fit for that. However, nowadays we also have web apps that are highly interactive.
This requires moving away from the thin client model. Server-side rendering approach is inherently at odds with that because HTTP is a stateless protocol.
So, the modern way to write web apps is to have a thick client that manages things like views, business logic, and state in one place. With this approach you no longer need a complex framework on the back-end because vast majority of the complexity moves to the client. The server is mostly responsible for things like data governance. The other driver here is horizontal scalability. It's much easier to scale small stateless services than a giant monolith.
Clojure started getting a lot of use in the wild right around the time when SPA model started taking off, and I think that the ecosystem reflects that. We tend to have fairly light weight back-end architecture coupled with a thick client on the front-end.
it already has reached an unprecedented level of adoption for a Lisp used in commercial software. But I agree with you - it would be nice to see it grow faster and bigger.
From technical evaluation - Clojure has an amazing ecosystem. But having technologically superior toolset does not guarantee success, nimlang is a good example.
What Clojure needs today is better marketing. It is fashionable to blame Cognitect (basically for anything), but the truth is - they are not a big company. Even if they really wanted it - they don't have enough people to drive it to wider success.
There are a few large companies using Clojure in production today, companies like Apple, Walmart and Cisco. It would be nice if they recognized the importance of Clojure and put some effort to evangelize for it, support initiatives like ClojuristsTogether, sponsor Clojure events.
> For developers looking for a language which will also help pay the bills Clojure is unfortunately not an option
From my own experience - the reality is the opposite. There are too many people interested in Clojure, but not enough developers with real Clojure experience. And companies using Clojure desperately want experienced Clojurists and often willing to pay more. It's disproportionate - we need more companies using Clojure, willing to hire juniors. We need to improve the funnel, making it easy to get into Clojure and become an expert fast.
if one ignores the 80s / early 90s of the AI boom
And poor startup times, and unhelpful error messages (though I hear there has been some improvement in recent versions).
[1] https://atom.io/packages/proto-repl [2] http://gorilla-repl.org/
Personally, I find maven repos and lein to be one of the better dependency/build ecosystem of any language. So I disagree with your premise. But, if you don't like them, there's a lot of alternatives.
For dependency management, there's now an official one, maintained and bundled with Clojure, called `tools.deps`. It supports multiple providers, currently maven repos, git repos and local folders.
For build tasks, appart from Lein, you can use Boot or Gradle. But there's a new trend now to rely on individual Clojure programs as the foundation for build tasks, which are then packaged and distributed through `tools.deps`. You can see their full list here: https://github.com/clojure/tools.deps.alpha/wiki/Tools
In the ClojureScript world, there are less build options, but `shadow-cljs` has emerged as a replacement for Lein, and gained a lot of popularity, because it integrates transparently with npm.
In addition, you do not need to use nrepl. Clojure has a socket repl and a socket prepl as of Clojure 1.10
It's a 4 year old project at this point, and in my opinion the easiest way to get started with cljs for a newcomer. It has sane defaults, and incorporates a ton of effort to "just work", such as with regards to the effortless way that you can pair it with npm or yarn to pull in arbitrary JS libs.
I'm also not sure what the problem with Leiningen is, it's a hell of a lot better than most build tools I've used. If you want to see brittle, you should take a look at NPM clusterfuck. Yet, people happily use that mess all the time.
Poor start up time is typically not an issue for web applications, which is the primary domain Clojure is used in. Meanwhile, at dev time you're using the REPL, so you're not restarting your app all the time. I often have an instance of the app running for weeks on end as I work on it. For environments where start up time matters we now have ClojureScript on Node and GraalVM.
The error messages are indeed a huge improvement in Clojure 1.10.
[1] https://marketplace.visualstudio.com/items?itemName=cospaia....
Even on Linux I have run into package management issues with lein1/lein2. I should say a build tool should largely be stable, not changing it behavior. On many functions like how you use local jars the features and methods have changed drastically at various points of time in Clojure/lein. This kind of churn has even effects like invalidating much documentation already written, and many stack overflow answers too. Why should an application developer be distracted by issues like these?
Most importantly if Clojure development is essentially depending upon lein, then I feel it should be distributed with Clojure itself. And emacs, cider, nrepl and lein chain has so many parts which are getting version upgrades independently, it breaks frequently for the user, functionality that was working suddenly stop working after an upgrade and you have to debug why it stopped working. Also I wonder how complicated it might be to set up the whole chain on a Windows machine.
Clojure now has dependency management built in with deps.tools that ships with it. It's simpler and less opinionated than lein. So, perhaps that will address the problem with getting started on Windows.
* actually three - if you include CLR
The Clojure system can be treated as a RMDB using the Lisp language, RMDB Schema (Clojure Spec) is a static type, and SQL (LISP) is a dynamic type.
It is an example of the best combination of dynamic and static types.
It's innovation, simple, practical and reliable.
https://github.com/linpengcheng/PurefunctionPipelineDataflow...
Clojure -> DBMS, Super Foxpro
STM -> Transaction,MVCC
Persistent Collections -> db, table, col hash-map -> indexed data
Watch -> trigger, log
Spec -> constraint
Core API -> SQL, Built-in function
function -> Stored Procedure
Meta Data -> System Table
In the latest spec2, spec is more like RMDB. see: https://github.com/clojure/spec-alpha2/wiki/Schema-and-selec...The main development goal of clojure is to write the database. The development idea is actually from the database, not the FP.
With reference to the database, as long as I use spec to strictly define (standardization) the core data model, I can ensure the correctness of the system.
I've turned the traditional work of ensuring code correctness from in-code type system validation to data self-validation. Turn the work into verifying the core data model instead of validating the input and output data types of the function.
Similar to industry, verify that all finished products meet the standards before entering the warehouse. Also similar to databases, verify their compliance before data enters the database.
That is "Data as a service, Warehouse as the core, operates around it".
A system requires only a few core data models, and development is built around the core data model.
Persistent data structures ensure that the modification of the immutable big data model are high performance and memory efficient.
In addition, using my pure function pipeline data stream (https://github.com/linpengcheng/PurefunctionPipelineDataflow) makes debugging, parallelism and extension very simple.
from: https://github.com/linpengcheng/PurefunctionPipelineDataflow...
This seems a tenuous theory. Clojure has been described as a language for data processing, but this isn't the same as basing the design on relational databases. Clojure isn't even relational; its data is generally kept in standard collection types, rather than relations.
I'd describe Clojure as a dynamically typed, functional Lisp that emphisizes open data models and explicit time.
Map to static type RMDB schema, similar to RMDB with schema and data as the core, with sql operation, simple and reliable.
Unrestricted open data model + dynamic type is not a good combination for constructing a complex large system.
Isn't that the same as relational databases once you dig a layer deeper?
```clojure
{:table01 {:row01 {:col-array [0 1 2]
:col-json "{\"a\": \"Hello\"}"
:col-text "abc"}
:row02 {}}
:table02 {:row01 {}
:row02 {}}}
```In addition, postgresql supports inheritance, which is also hierarchical nest collection types.
Clojure doesn't have good tools in its core library for working with relational data. There's no core type that explicitly represents a relation, and Clojure lacks functions for many basic relational algebra operations. For example, how would you perform a natural join across your data structure?
Convert(or design) data to hash-map, join (or merge) by key.
it can write commonly used operations as functions, try to row (or col) operations as much as possible, and join all data only when necessary(reduce the row-join).
```clojure
(def a {:a-id-01 {:a-name "a1"}
:a-id-02 {:a-name "a2"}})
(def b {:b-id-01 {:a-id :a-id-01 :b-name "b2"} :b-id-02 {:a-id :a-id-02 :b-name "b2"}})
(->> b :b-id-01
:a-id
a
:a-name)
;=>;"a1"
(let [x (b :b-id-01)]
(->> x
:a-id
a
(merge x ,)))
;=>;{:a-id :a-id-01,
; :b-name "b2",
; :a-name "a1"}
```
Relation is a logical model mapping of data structures, it is just a logical thinking that exists in the brain.
SQL, Prolog, clojure.core, minikanren can be used for relational operations.
It's true that we can represent relations using collections. In Clojure we'd write:
(def a
#{{:a/id 1, :a/name "a1"}
{:a/id 2, :a/name "a2"}}
(def b
#{{:b/id 1, :b/name "b1", :a/id 2}}
But these structures don't allow for efficient lookup or joins, and we lack inbuilt functions to easily deal with data modelled in this way.Relational databases are based on relational algebra. If Clojure is based on relational databases, then we'd expect to be able to do relational algebra easily in Clojure. But we can't: the core library isn't designed for it, and the built-in data structures aren't designed for it.
1. arbitrary layering and deep nesting are not good engineering practices.
2. refer to the data-model & code of my latest two posts. I prefer to use hash-map as the table with the primary key hash index, with key as the primary key and val(colname-colval-hashmap) as the row content.
I also don't think relational algebra operations must be implemented in the form of RMDB and SQL. It can also be implemented very elegantly with clojure.core. using hash-map operation is simpler, clearer, smoother and high performance.
There are many ways to implement relational algebra. The thinking is not limited by the "information structure" displayed by the traditional RMDB interface. In clojure, the hash-map(NoSQL) is the underlying physical model, and the relational model is the upper logical model.clojure core function acts as a data manipulation language, I named this architecture SuperSQL or SuperRMDB.
In fact, the original data manipulation language of posgresql and foxpro is not SQL. Clojure core function is closer to foxpro's commands (DML).
3. set, vector, list is generally not a good default data container, only used when needed. you use the set as container, it's difficult to operate data (table, row, column, value).
4. In summary, I think: programming is the process of designing a data model that is simple and fluent in manipulation. To have open thinking, not to be restricted by traditional thinking, to be flexible, adapt to local conditions, and design as needed.
Perhaps, but that's irrelevant; I'm describing the difference in how hierarchical and relational models are designed.
"I prefer to use hash-map as the table with the primary key hash index, with key as the primary key and val(colname-colval-hashmap) as the row content."
And what if you need a second index? Your indexing should be separate from your data model, otherwise you can't write performant relational algebra operations that apply in the general case.
"It can also be implemented very elegantly with clojure.core. using hash-map operation is simpler, clearer, smoother and high performance."
No it can't. Suppose I have a relation with keys: a, b, c, d and e. I want to index on a, b and the pair (c, d). How would I do that in Clojure? What happens if I later decide I also want to index on e?
This is the sort of problem that's trivial to solve in a relational database, and extremely hard in Clojure, because Clojure doesn't have the functions or data structures to support data modelled in this way.
That's not to say that Clojure can't have these tools; just that they aren't built into clojure.core, because that's not what it's designed for.
"set, vector, list is generally not a good default data container, only used when needed"
Yes they are. Sets are the basis of relational algebra.
You're complecting the ideas of data representation with data indexing. Sets are a good representation of a relation, but a poor index.
We can get the best of both worlds by combining the two:
(def a
(let [r1 {:a/id 1, :a/name "a1", :b/id 1}
r2 {:a/id 2, :a/name "a2", :b/id 1}]
{:relation #{r1 r2}
:index
{:a/id {1 #{r1}, 2 #{r2}}
:a/name {"a1" #{r1}, "a2" #{r2}}
:b/id {1 #{r1 r2}}}}))
A data structure like this allows us to start writing efficient relational algebra. For example, with a natural join we can look for the smallest index two relations have in common.So we can begin to construct the infrastructure we need to perform relational algebra in Clojure, but it's not there to begin with, and therefore Clojure isn't designed around the relational model.
Implementing an RMDB is not equivalent to relational algebraic operations. I think it should be to use simple, direct and lightweight the relational logic model to solve real-world problems. Don't over-optimize, over-generalize and over-complicate, keep it simple and direct.
Just like we design a Database in RMDB to solve real-world projects, This is the normal way to use relational algebra and models. After the database design is complete, we don't need care how the index is implemented, and we don't need care the underlying storage of the data. I mean, clojure is used as RMDB , is not used as tool of construct RMDB .
Therefore, my method is to design the application-level data model. The problem you said does not exist. When I get the data from the database (or elsewhere), I simply transform the data to the target model, you can think of this model as a table or view, you don't need to transform again, so you don't need multiple indexes.
Like most general-purpose programming languages, Clojure has a hierarchical data model. We have a number of collection types, and we can put any collection into any other collection.
A relational model takes a fundamentally different approach. Relational data is represented not by nesting collections, but by a flat set of tuples. Efficiency is achieved through indexing, not by rearranging collections.
There's some interesting research around on using the relational model outside of a database, but that's not a design goal of Clojure.
```clojure
(def table01 {:t1-pk1 {:pk :t1-pk1
:name "t1-r1"
:manager :m1}
:t1-pk2 {:pk :t1-pk2
:name "t1-r2"
:manager :m2}
:t1-pk3 {:pk :t1-pk3
:name "t1-r3"
:manager :m3}
:t1-pk4 {:pk :t1-pk4
:name "t1-r4"
:manager :m2}})
(def t1-manager-index {:m1 #{:t1-pk1} :m2 #{:t1-pk2
:t1-pk4}
:m3 #{:t1-pk3}})
(->> :m2 t1-manager-index
(select-keys table01 ,))
; =>; {:t1-pk2 {:pk :t1-pk2, :name "t1-r2", :manager :m2},
; :t1-pk4 {:pk :t1-pk4, :name "t1-r4", :manager :m2}}
(->> [:m2 :m3]
(select-keys t1-manager-index ,)
vals
(apply clojure.set/union ,)
(select-keys table01 ,))
; =>; {:t1-pk2 {:pk :t1-pk2, :name "t1-r2", :manager :m2},
; :t1-pk4 {:pk :t1-pk4, :name "t1-r4", :manager :m2},
; :t1-pk3 {:pk :t1-pk3, :name "t1-r3", :manager :m3}}
```
Your point of view is mainly to emphasize that Clojure is a multi-paradigm, general-purpose functional programming language.
The postgresql development team is also this view, so postgresql is not only RMDB (relational modeling), but also supports OO and json (NoSQL). But postgresql default data modeling is relational modeling
My point of view is mainly to emphasize the best practices of data modeling and programming.
Both views are correct and can exist in parallel.
No, that's not my point at all. I'm saying that Clojure's core library and data structures are built around a hierarchical data model and not a relational one.
If you want to model your data as a relation, then you need to build the tools and structures for it. Look at your code, then consider how it would look in a language designed around relational algebra:
(def table01
#rel [{name "t1-r1", manager :m1}
{name "t1-r2", manager :m2}
{name "t1-r3", manager :m3}
{name "t1-r4", manager :m2}])
(select table01 (= manager :m2))
; => #rel [{name "t1-r2", manager :m2}
; {name "t1-r4", manager :m2}]
(select table01 (or (= manager :m2) (= manager :m3)))
; => #rel [{name "t1-r2", manager :m2}
; {name "t1-r3", manager :m3}
; {name "t1-r4", manager :m2]
An indexed selection against a relation would just be a single function or macro. We wouldn't need to mess around with select-keys and set union to achieve such a simple operation, as you needed to do in your code. It would be built into the core library or the language.I want to emphasize that I'm not saying you shouldn't model data the way you are. There are plenty of advantages to it. But the more you go down the relational rabbit hole, the less suitable clojure.core is to handle it.
Clojure is about data modelling and processing, but it isn't based on a relational model, as you suggest:
"Clojure is a functional programming language based on relational database theory"
Clojure is a functional programming language, but it's not based on relational database theory.
I had implemented a DataFrame with hash-map, which implements relational operations. A relational operation is just a function. The advantage of hash-map is that key-chain can be used as a pointer, and processing data elements is simple and efficient. Therefore, DataFrame has advantages of RMDB and NoSQL.
A strict relational model will lose the flexibility of data element operations.
[0] for example, nobody uses the STM implementation
I'm baffled: give people a powerful tool that can simplify and bring enormous joy to work with two, most popular platforms of all time, even making it possible to share code between completely different worlds - they'd still be complaining about it.
https://insights.stackoverflow.com/survey/2019#technology
As much as I love Clojure, it's still a harrowing language for beginners to learn and use. The learning curve is just too high. If you have to learn category theory in other to handle basic programming flows, then there's just something wrong.
Elixir, for instance, has many functional primitives, but isn't dogmatic about it. It can "feel" like traditional OO-style programming without the cruft.
I say this as someone who uses CLojure and has at best a very sleight understanding of category theory.
I have worked with several people for whom Clojure was the very first PL they have learned and they were very successful. One thing is nice about Clojure: it stays consistent - the idioms, data structures, conventions don't suddenly change. In most other languages - you pick up a framework, you try to learn it and it feels like you are learning a whole new language.
Yes, Clojure can be very intimidating and confusing for the beginners but its learning curve is not steeper than of Java's, Ruby's or Python's.
[The Pure Function Pipeline Data Flow](https://github.com/linpengcheng/PurefunctionPipelineDataflow)