My personal favorite is add-libs
You can now write single file demos or minimal examples for issues. Really lowers the friction to share small runnable snippets with people
You can also actually use it to demo Java libraries as well without all the Java boilerplate. Just poke around in the REPL and paste code into a comment on HN or wherever and anyone can replicate your "setup" and get it running exactly the same. No need to clone a repo or anything
You run `clj` to get a Clojure REPL
Then you can for instance paste the following into the REPL
(add-libs {'thi.ng/geom {:mvn/version "1.0.0-RC4"}})
(use 'thi.ng.geom.viz.core)
(use 'thi.ng.geom.svg.core)
(->> {:x-axis (linear-axis
{:domain [-10 310]
:range [50 550]
:major 100
:minor 50
:pos 150})
:y-axis (linear-axis
{:domain [0 4]
:range [50 150]
:visible false})
:data [{:values [[0 100] [10 90] [80 200] [250 300] [150 170] [110 120]
[210 280] [180 280] [160 240] [160 170]]
:attribs {:stroke-width "10px" :stroke-linecap "round" :stroke "#0af"}
:layout svg-stacked-interval-plot}]}
(svg-plot2d-cartesian)
(svg {:width 600 :height 200})
(serialize)
symbol)
- the first line downloads a library and adds it to the running session- the second and third line add the library to the REPL's default namespace
- the rest of the code makes an axis and plots some random values
- the output is then serialized and send out the REPL output
You should get a little SVG plot on the output
PS: Make sure you have version `1.12` by running with `clj --version`. If not, then (re)run the instructions here to get the latest version: https://clojure.org/guides/install_clojure
It'll pop up a little window displaying an image
(add-libs {'org.boofcv/boofcv-all {:mvn/version "0.35"}})
(import 'boofcv.alg.color.ColorRgb
'boofcv.core.image.ConvertImage
'boofcv.gui.ListDisplayPanel
'boofcv.gui.image.ShowImages
'boofcv.io.UtilIO
'boofcv.io.image.ConvertBufferedImage
'boofcv.io.image.UtilImageIO
'boofcv.struct.image.GrayU8
'boofcv.struct.image.ImageType
'boofcv.struct.image.Planar
'java.awt.image.BufferedImage)
(let [image-url "https://kxygk.github.io/web/chengdu.jpg"
color (ConvertBufferedImage/convertFrom (UtilImageIO/loadImage image-url)
true,
(ImageType/pl 3 GrayU8))
weighted (GrayU8. (.width color)
(.height color))
gui (ListDisplayPanel.)]
(ColorRgb/rgbToGray_Weighted color weighted)
(.addImage gui
weighted
(str (.width color)
" "
(.height color)))
(ShowImages/showWindow gui
"RGB222"
true))
I recall that Atlassian had some kind of scripting console where you could run Groovy and interact with its API / objects. It was useful for exploring when writing plugins for bamboo or Jira ...
In fact, I think it's confusing to call the repls of language like Ruby, Java, JavaScript et al "REPL" at all, compared to what lisps offer, as the experience is so different. They're more like "Code evaluators" than anything else.
Typically when using repls outside of lisps, you'd type your code into the actual repl window, while in lisp-land you typically connect your editor to the repl and program like usual, selecting stuff to evaluate and so on.
I think that's neither typical in the history of Lisp nor in other languages. For example in Mathematica and Jupyter one interacts via a Notebook interface. Smalltalk interacts via an integrated IDE. Many editors connect via LSP to language servers.
In Lisp the idea of an editor connecting to a Lisp is a bit of a historic accident. After the AI winter the Lisp development environments were no longer used.
A typical way to start Lisp from an external editor was via the editor starting a subprocess (the "inferior Lisp") and talking to it. GNU Emacs did that with IDEs for Lisp, like ILISP.
The next generation used GNU Emacs as its external development environment (instead of developing a new one in Lisp) via a network interface. SLIME was the most important one, with SWANK being the component loaded into the external Lisp, which provided a server interface.
Unfortunately the SLIME REPL itself (its read eval print loop) is not great (in general SLIME is okay to use) and other languages sadly copied the SLIME model, failing to copy various other important REPL features of Lisp. For example Clojure lacked the error handling of Lisp REPLs, which typically provide features like break loops, where one can inspect and repair errors. SLIME goes directly into a backtrace window. Thus people copied it, without knowing that there is a tradition of completely different REPL interaction. GNU Emacs is bad at multitasking, thus one REPL buffer could block the editor for a while -> multiple REPL windows were not that useful. Another way of working got lost...
> Another way of working got lost...
You singled out SLIME, but you've mentioned before that you use LispWorks - do you think it's also "not great"? Just curious ...
Especially the REPL of SLIME / GNU Emacs.
LispWorks provides a better REPL experience.
I'm faster and more in the flow with the LispWorks IDE.
Agreed. I always call them “consoles,” since I work largely in JS and that’s what the browser calls it. There’s no way in a console to jump into a module and modify a single function without resetting any state that the module was tracking.
An IDE connected to a running Java process via JDPA/JDI (Java debug interface) is probably a better comparison.
Sure, Common Lisp will allow you to load more substantial code changes into the running process, but using JDI you can "break on specific/ all exceptions" which will allow you to inspect code before the stack is unwound, you can drop frames and re-run stuff possibly after changing values.
What's funny is that I started noticing and using these possibilities at my Java job while learning Common Lisp. Before learning about Common Lisp I was almost exclusively an edit/compile/run guy, now I often take the shorter route of edit/CTRL-SHIFT-F9.
However that doesn't mean Java REPL is useless.
Still waiting on spec2, though... for now I am working around rigidity of specs by using Malli but it really isn't a first-class citizen in Clojure (mostly due to inability to check macros, and this is by design of the Clojure compiler). But you can emulate the ideas of schema/select by manipulating malli schemas as data.
The changes for functional interfaces also means we no longer have to maintain utility macros like
(defmacro ->Consumer [f]
`(reify java.util.function.Consumer
(accept [this arg#]
(~f arg#))))
and instead just pass functions directly.https://github.com/clojure/clojure/blob/ad54fec/src/jvm/cloj...
The Clojure compiler directly calls into clojure.spec and does not expose any sort of hook for validating macros. No library can validate macros except clojure.spec. In this sense, Malli feels like a second-class citizen in Clojure compared to the built-in clojure.spec.
This release feels like it had a very different scope from previous releases, contains a lot of stuff, which is exciting to see! But I hope it doesn't end up like a hairball a few releases down from increase of pace or something.
Rich Hickey and the rest of the Clojure Team are very careful designers. I wouldn't worry too much
I am aware of this, which is why I suppose previous releases haven't included as many features at once as this one. It's this possible change of pace that raised the question in me :)
In fact, the separate libraries have their own release process and isn't part of this release at all, as things should be. So not sure what you're referring to here exactly?
That the size or pace of this release alone should cause worry about future hairballs.
I'm not trying to troll. I want to choose it. It seems like a good engineering decision to me, but if it's nosediving in popularity and contributors, this might bite me back in the near future.
So what you don't see is a constant flux of "innovation" followed by a community having to adapt to those innovations. People pull Clojure examples out of books that are 12 or more years old and they still run.
I think there's some very exciting things in the Clojure space, such as Clerk (https://github.com/nextjournal/clerk) for live notebooks, and Babashka (https://github.com/borkdude/babashka) for amazing (and amazingly fast) scripting.
If someone tells you their project is written in Scala, Golang, Groovy, Coffeescript it almost dates the project doesn't it? Not so much in Clojure.
It's niche but I can bet it's still going to be there 10 years from now, going at least as strongly as now.
Even as I flip through the 7 postings mentioning Clojure in all of Canada, only 4 of them seem to indicate the job itself makes use of the language (rather than mentioning it just as an example language as in "* Fluency in one or more languages like Ruby, Clojure, Scala, ReactJS, JavaScript, TypeScript, Java, Python - Deep understanding of internet protocols and standards.")
I certainly wouldn't.
The good news is nothing really _breaks_ ever, and you have access to the entire JVM ecosystem if you want it (many Clojure people find Java interop icky which I personally find moronic).
Similarly to how Groovy used to be, but I am not counting it as it seems only to be around to power Gradle and little else.
Scala and Kotlin folks speak too much about replacing Java, yet I am yet to see their native variants match JVM in any form or fashion.
Even if we take Android into consideration, Google was forced to update their Java support, as means to keep Java libraries ecosystem available for Android developers and their beloved Kotlin.
I love Clojure, and it's been great for me and all the professional teams I've worked on that use it.
Many clojure libraries are simply done. There is no reason to commit everyday to a project.
https://github.com/oakes/odoyle-rules/
I think the userbase is slowly shrinking, but I'd personally use Clojure even if all Clojure developers disappeared tomorrow. It's not built on shifting sand as the JVM is stable. If you need really niche/cutting edge stuff you're probably going to need to dip into Java or JS interop anyway
Sad to hear about your experience.
> A lot of abandoned libraries. There's not a huge ecosystem around it compared to golang
I found it to be the opposite. In Clojure you have Clojurists Together: https://www.clojuriststogether.org which funds people to work on Open Source libraries. And more importantly there is Clojure Commons: https://github.com/clj-commons which takes popular Clojure libraries, that are no longer being supported and carries on looking after them.
When I found popular Go libraries that have been abandoned, I asked in the Go community, if there are such initiatives, especially seeing how Google is behind Go. When people didn't understand what I meant, I pointed them at the examples above, from Clojure. To which their response was "TIL, Clojure, never heard of it before! No, we don't have such initiatives in Go."
Maybe the initiatives from Clojure Commons & Clojurists Together need more visibility for the newcomers to Clojure?
I don't see an easy answer to this because having "done" be an available state is extremely attractive, and forcing extra work on the author would be the wrong thing to do.
The one example I can think of where that's emphatically not true is clojure-android, which was tightly coupled to the Android SDK and did not remain usable after the main developer moved on. The Android SDK does not share the aforementioned traits.
Find a random node package that was last changed 4 years ago and it might as well be toxic waste.
Find a clj lib with the same stats and it's probably going to work smoothly.
There are opportunity costs too, maybe you can say Spec is perfectly fine but you’d be forgiven for having mixed feelings with a massive codebase built on that in a world where many have moved to Malli or elsewhere.
But let’s also be honest, loads of promising Clojure libraries do get abandoned, not finished. ClojureQL was a brilliant, composable approach to writing SQL queries but never reached its potential and was left in a fairly buggy state. I’m probably four or five Clojure data access libraries on from that in my career now. Om was highly influential but good luck if you made an investment in that. Incanter could have been enormous but failed to reach critical mass and you’d be crazy to pick it up now. There’s ‘core’ stuff like core.logic that feels like a decade old unoptimised proof of concept that didn’t follow up any of the interesting paths it laid down. Heck, I’ve even got code that relies on libraries from Chris Zheng, who quit the scene after getting a deserved dressing down from Rich Hickey after complaining about the Clojure development process.
None of this is a moral failing on the Clojure community, and there’s no reason to be defensive about it. It’s a small ecosystem and it’s very hard to build critical mass. Clojure people often have magpie brains that pull them to work on new things. You’ve got to judge it for yourself.
"As of this writing (25-Sep-2014) I consider this library alive and well"
... "Update 2017-Oct-16: Still true. :)"
This library has been done-done for a decade now. I used it last week.
The entire ecosystem could die tomorrow, and you can still compile new Clojure code for any system that supports Java in the future.
A major problem people have at first[0] is just reading the code. AIs like ChatGPT and Claude are incredibly good at explaining existing Clojure code.
As a result, developers can onboard much, much faster.
[0] After a few weeks, reading Clojure becomes second nature and you'll forget you ever COULDN'T read it.
The functional interface coercion is implemented with invokedynamic.
[1] https://youtu.be/2V1FtfBDsLU?feature=shared&t=639 [2] https://scicloj.github.io/ [3] https://github.com/HumbleUI/HumbleUI
It can easily interop with Java and do the same stuff that Java does ('enterprise stuff' and more).
It excels at handling data transformations, so it's popular for tasks involving complex data pipelines, analysis, or real-time processing.
Clojure’s immutable data structures and concurrency story make it great for building highly concurrent systems, so things like financial services, real-time monitoring, event processing.
Clojurescript compiles to Javascript and interops with it well, so web based front end applications. It's used for mobile app development too.
There are libraries for AI/data sciences and there's python interop via library too, so you can build Clojure apps on top of Python libs.
Native-fast shell scripts using Babashka.
I would not use it for low level system programming (eg. where C is used), but for everything else - it would be one of my first choices.
It's a very pragmatic and practical programming language, with a strong philosophy behind every design decision and the fact that it can interop with pretty much everything makes it a powerful tool in skilled hands. Learning curve is quite steep though, but well worth climbing it.
NuBank, OneStudyTeam, CircleCI, and Guaranteed Rate are some of the larger companies I know about personally that have significant investments in Clojure.
Pretty much anywhere where the jvm is a good fit, but given it’s a hosted language you can also use it to emit dart code or js and find it running on shoulders of others (like jank, llvm based dialect, or babashka)
$5M ARR. Two full-time developers.
* No one was in love with Java. It was fine but we were doing the whole spring style super verbose Java and it felt like a lot of ceremony to get anything done. There had been an experiment with Scala previously but that hadn't taken off.
* We had a service-oriented architecture which meant we could try Clojure out on a few new services and see how it felt.
We ended up going from 2 services to moving the whole org over really quickly. A lot of excitement built up and people didn't want to be left out. At the end of things only 2 people decided they didn't want to learn Clojure.
A few other things we did:
* Bought loads of books and left them lying around
* Started a Clojure club where we booked an hour a week and did some exercises in Clojure
* Had a big meeting where we convinced everyone that Clojure was worth an experiment
* Brought in 3 consultants to do some Clojure training for everyone
* Probably strong armed everyone into watching simple made easy - it helped that lots of people had already seen it live that year
There are a few talks about it floating around although they are very very old now and I'm not sure they're worth the time!
https://whostolebenfrog.github.io/clojure,/deployments,/clou...
At strategic locations? Such as the bathroom?
Ideally, use someone well versed in the tech stack - otherwise, you're taking on double risk of a new tech stack plus unfamiliarity. This prototype needs to demonstrate the value of the clojure stack - which has to have a business value (aka, speed/ease of maintenance etc).
Convince the decision makers to use clojure after the above prototype showed value, and use it to extrapolate value for other projects. This will then require a transition from doing the project, to teaching others (who might not know clojure at all). You cannot rely on just telling people to watch videos or read books - they won't do it, and it will cause failure in subsequent projects.
You will have to hand hold, until the newbie "clicks". Unfortuantely, this is an uphill battle, because management will always want to hire for the "regular/normal" tech stack, and will have trouble finding clojure-ready people. So the company will continue to have to invest in teaching it to new hires, which is a drain that could tank the clojure move.
I do like Rich's talks and agree that static typing can be taken a bit too far though.
ClojureScript in particular is much nicer to work with compared to JavaScript
Forcing it on your team could create frustration, confusion, and a lot of resentment, not just toward the language, but possibly toward you. Sure, you might convert a person or two, but most will likely see it as unnecessary complexity.
If you really want to write Clojure every day, you might be better off finding a company that already embraces it instead of trying to turn your current team into Clojurists. Sometimes it’s better to enjoy something for yourself than to make it everyone else’s problem.
At a minimum, build some strong clojure skills first, such that when you introduce it to anyone else, you can do so with confidence
I’ve vowed since then to stick it out, to work hard building something better at the companies I work at, and to make it obvious from my resume and day to day that I care about being somewhere long term. I take the idea of leaving my campsite better than I found it seriously, and if Clojure was just a fun hobby for me I wouldn’t try to introduce it at work. I think my colleagues can learn a lot from the language and paradigms that are at the core of the language, my hope isn’t that we re-write our stack in Clojure, or even that the services I write in it persist past when I leave. My hope is that I can use it as an example to show the other engineers I work with a better way, even if we do it with go in most of the stack.
I personally have sat in a meeting where a clojure enthusiast was explaining how he had quickly built a system.
He could not explain the code he had written only three weeks before.
Clojure suffers heavily from a “less code is good code, write clever code not verbose code” culture.
For newcomers this creates dumpster fires that are quick to create and impossible to maintain.
Anywaaay, long story short: no. It doesn’t.
Specifically, I’ve seen it be a dumpster fire three times.
Perhaps your experience has been different; but you are flat out wrong in your generalisation; and that arrogant attitude has been the root cause of all three failures I have personally had to clean up after.
Less code can be good code, but they largely are orthogonal axes. Without good abstractions, it doesn't matter whether code is dense or verbose, it will be bad and difficult to grok.
Over time and at scale, this matters quite a lot. Java code grows and grows at a super linear rate as it handles new and changing requirements. This is ultimately not sustainable. Clojure code typically grows at a more linear rate (accretion of attributes in data or operations on data), but has more tools to create abstraction that can actually (if wielded well), be sub linear instead. This kind of change is not free or easy in any language, but in Clojure it is at least possible.
You can write code that looks a lot like Clojure in any modern language now, a lot of the functional primitives have been adopted in mainstream. e.g. Async/await => channels.
I started my career as a Clojure/Clojurescript developer 12 years ago. And now I do python for ML research and Typescript for prototyping all day.
Clojure honestly help me back a lot of the time because the tooling is so far behind the large programming communities. Functional programming has a lot of good ideas, but none good enough to leave behind all the packages on pip/npm.
I would only ever advise niche programming languages for small sub-teams that are highly technical were it makes a lot of sense. Something like compiler teams, webgl/triton for GPU programming, etc...
At least for npm you don't have to do this. With shadow-cljs, nbb, and squint, you now have good mature options for consuming npm packages.
I see nil panics a couple times a week, in prod. We could learn the methods and apply them in other languages, like other people said they did in this thread.
Clojure (and indirectly Java) was never created with "fast startup speed" in mind. This is why things like `add-lib` (that was added in this release) is so useful, because you start your developing session once, and it stays open until you either have to restart your computer or done developing the project.
Then the typical deployment target is either "Push this .jar to a server somehow, start the process and let it run" or "User double-clicks executable, waits for application to be ready".
If you really need fast startup, you can go through something like https://github.com/ionutbalosin/faster-jvm-start-up-techniqu...
Or if you're fine with slightly worse runtime performance, give https://github.com/babashka/babashka a try
Finally, if you're OK with JavaScript you could give ClojureScript a try, has fast startup (as fast as NodeJS I suppose) but yeah, it's JavaScript.
But overall, Clojure/Java isn't optimized for the use-case of "Start process for each request, stop process once processed" so I'm guessing you'll face an uphill battle there.
But from java world: With GraalVM/NativeImage, it is possible to compile (clojure or java) .jar files into native binaries.
Those binaries have a way faster to startup (less than 10ms for non-trivial apps)
Also, lambda engines (like AWS lambda) continuously improve their "java" target, with preloading and other things,which also benefits clojure.
TLDR; clojure startup isn't a thing that needs to be fixed. JVM startup used to be slow. And this is being resolved in the JVM itself.
Note: clojure DEV startup time is still slow-ish. Clojure DEV is usually not AOT compiled and etc. the new "sync deps" feature helps to solve this DEV problem