Nice article in general, but seems to miss the most important point (at least for me) of why Clojure (and similar languages) are way better than anything else today: it's REPL driven development. Definitely more important for me than any of the other "why we love Clojure" points.
As long as Rust doesn't offer something similar as the REPL driven development Clojure offers, there is zero reason of preferring Rust (for me) over Clojure for the same tasks. Which, because of the last point ("Rust is not homoiconic") won't happen any time soon.
Now, if you need to do embedded system development, Rust might make more sense than Clojure, but it won't replace Clojure for the same tasks today.
RDD is the one big thing that really blows me away in Clojure. I now feel quite frustrated having to work in other languages where I'm missing the tight REPL integration.
It's ideal for getting into "flow", iterating on ideas, there's no waiting or downtime that you can get in other languages.
I don't know why REPL driven development and Clojure type REPL integration isn't how we all want to do development (assuming as you say it's not embedded or some other niche area). It's just so powerful as a tool for getting immediate feedback and allowing a tight, painless iteration on ideas.
I've only dabbled in Clojure and REPL-driven development, but is there a way of easily converting a REPL sessions into a suite of tests?
Coming from more of test-driven development background, I found that tinkering in the REPL was similar to what I would usually do with unit tests, but without the ability to easily build up a test suite that would prevent regressions in future.
I'll start by writing a bunch of tests and have a test runner that runs all my tests everytime I change my source code and displays the results. It's fast, I have one project that runs ~1400 asserts and on each save it shows the results less than 2 seconds later.
THen I write the code in my IDE source editor and use a keyboard shortcut to send it to the REPL as I'm working. I wouldn't ever write code directly in the REPL. By the time I've got it working in the REPL I normally then see that all my tests are passing.
Depends on how you use your REPL. I run the REPL in the background and connect vim (via conjure plugin) to the REPL, so I still type out my code into my editor, into files and buffers, and only save them to disk when I'm happy with the code.
So usually I end up mostly living within (comment) blocks around the functions I'm interacting with, and when I'm happy with it, move the code into the correct test namespace, which is a matter of just moving the code around and changing references to namespaces.
I’m a beginner clojurian but i’ve noticed some devs put a repl folder in their source tree, e.g. stuart sierra’s component project has a dev sub folder https://github.com/stuartsierra/component/tree/master with repl examples in addition to the tests.
I missed a trick in life, i started getting into Clojure in 2020 but about 5 years ago i used to sit 1 row of desks away from Stuart Sierra in Stirling and I don’t think i ever properly spoke to him save for hello or so. It was a funny setup in that operation, i had to fight for my small team to even have office space at the time!
Good callout! Adding "dev" files for facilitating REPL development is one way, adding something called "rich comments" is another one (that I tend to gravitate towards) where you have comment blocks (actual code, not commented code) in the same ns as the actual functions, exploring the usage of the functions. Was recently on the HN frontpage as well, https://betweentwoparens.com/rich-comment-blocks - https://news.ycombinator.com/item?id=24718478
Have you ever tried playing with OCaml? It's obviously not the same thing as a LISP, but its type system is similar to Rust (Rust was largely inspired by OCaml) and it's (much) more amenable to playing in the REPL.
I have not, as last time I checked, there was no vim plugin for editor integration with the OCaml REPL, but did another search now and found something that seems to make it possible (https://github.com/jpalardy/vim-slime) so might test it out at one point. OCaml always looked interesting, but now I could actually try it so thanks for the reminder!
I don’t know. I worked in Clojure for several years, and very seldom hit the REPL. I spent most of my exploration time in tests. In TypeScript, I split most of my exploration time between types and tests. Occasionally I hit the Node REPL, or the Chrome console. But doing speculative work in an editor, with all its features, has always felt a lot more productive to me. Maybe that’s why I ended up drifting away from Clojure anyway.
> speculative work in an editor, with all its features, has always felt a lot more productive to me
Yeah, I agree with this, which is why I spend all my time in the editor, which is connected to the REPL. I never leave my editor to write stuff manually in the REPL, I simply send the forms from my editor to the REPL to be evaluated. So the best of both worlds :)
What's on the left is vlaaad's editor, and the REPL is on the right. Usually I have a similar setup, but the output of the REPL goes straight into my editor instead of in a separate window.
One question for you, if you rarely used the REPL, how is your workflow with Clojure unless with a REPL? If you're working on a service, do you restart the process after each change or how to you avoid the REPL when working with Clojure?
Well I don’t work in Clojure at all anymore. But I guess this isn’t substantially different from how I work. The biggest difference is that I spend most of my time sending whatever speculative work to the compiler (TypeScript) or the test runner. Both of which are run in various forms of watch mode. When I worked in Clojure that’s pretty much how I worked then too. My flow now is almost entirely contained in VSCode, and the biggest change since I switched from Clojure is that I’ve embraced the IDE, static typing and a debugger.
I very rarely stray far from real TDD (red -> green -> refactor), and I very very rarely run whatever service I’m working on. The big exception is when I do (web) frontend work, which used to be my bread and butter but has been fairly limited for a few years.
That was the problem right there. Not getting used to a REPL driven workflow in Clojure is like using TypeScript without an IDE. It'll negate all benefits.
I don't know. I quite liked working with Clojure at the time. My previous experience had been:
1. PHP
2. JavaScript (browser)
3. JavaScript (Node)
The introduction to functional programming was eye opening to me, and made me a significantly better programmer. Working with ClojureScript, on the other hand, was painful. Numerous hours-long debugging sessions where I discovered I was passing a different type to core functions than I expected, causing wildly unexpected and baffling errors. Reporting issues and having them closed with "wontfix" because "don't do that".
That's what led me to TypeScript. And then of course the IDE. But I carried what I learned from FP with me. And now, having learned a powerful type system, it's made me a much better programmer still.
I don't mean the REPL is the only upside to Clojure, but it's the trade-off for the lack of static type checker with IDE.
If you needed to run the TypeScript compiler as a CLI and it spiled errors on the command line, that also wouldn't be a very pleasant experience.
I've known a few people coming from Java or C# to Clojure, never really getting into the REPL, would mostly program the same way they used too, and they went back. Which frankly doesn't surprise me, I'd go back as well if that was the case. The dynamism of Clojure isn't there out of lazyness to implement a static checker, but to be leveraged both at run-time and development time.
Clojure is still a lovely language without it, but if you don't leverage the dynamism, then it'll seem like the language is lacking staticness, obviously, since you're using it statically except it has no features built around staticness.
That said, I don't blame anyone for not leaning into the REPL, Clojure and especially ClojureScript doesn't make it easy, the tooling is rough around the edges.
As an aside, I think everyone should explore multiple paradigms and languages, and having good experience in a statically typed language will make you a better programmer, even if you were to go back to Clojure one day. The act of thinking about the types behind your variables actually makes you a better programmer in dynamic languages, because you start to have a static checker inside your head, and over time make less and less type related mistakes or can catch them very quickly.
In Emacs, there is tight integration between the editor and the REPL. You can write code in an Emacs buffer, and you can enter that namespace from the REPL and the code from the buffer is immediately available. You can also add new code directly from the REPL, but I usually just add what I want to an Emacs buffer and eval it. It's a very productive workflow.
I've found I can get the same tight feedback loop at least for the times where the code will not compile. I use Cargo mode and an after-save hook so that each save runs `cargo check` and, on success, also runs `cargo fmt`. A nice side effect is that I don't need to care about formatting any more.
As long as Rust doesn't offer something similar as the REPL driven development Clojure offers, there is zero reason of preferring Rust (for me) over Clojure for the same tasks. Which, because of the last point ("Rust is not homoiconic") won't happen any time soon.
Now, if you need to do embedded system development, Rust might make more sense than Clojure, but it won't replace Clojure for the same tasks today.
reply