clojure

Gosha 2025-09-14T12:39:49.364279Z

What do folks suggest as a standard formatting tooling setup for a team? We’re working on a large codebase that’s been mostly written using IntelliJ/Cursive, but we now also have people using Vim and Emacs. From what I understand, Cursive has a built-in linter/formatter, as well as a specific commenting style (single semicolon comments everywhere), and this has become the defacto standard in our codebase. Each dev environment has a different setup though, so we’re having large diffs which are basically one form shifted two spaces to the left, or similar. In my own projects I’ve been using @chrisoakman’s excellent standard-clojure-style-js but it doesn’t work here because it’s not configurable (by design) and doesn’t match the cursive style. What are my options? cljfmt? zprint?

Ovi Stoica 2025-09-15T08:07:02.055889Z

I think the best solution is that standard Clojure is supported in intellij as it is the most minimal of all formatters. It was stated above that formatting doesn’t matter that much. I’d say, it should cover things like removing extra spaces, fix miss aligned keys in a map (miss alignment caused by extra space on one of the keys for example), remove extra empty lines, (maybe) arrange namespace imports Horizontal scroll limits I think are optional if the devs have common sense. I never saw Clojure code lines past 120 chars tbh - to me this is a non issue but if you start a discussion about it in order to arrive at a mandated max char Len you usually end up with a lengthy discussion that doesn’t give good results

2025-09-15T11:03:21.124079Z

We had the same situation at Sharetribe. Fairly large codebase, mostly Emacs users but also IntelliJ/Cursive and VSCode/Calva users. A couple of months ago, we decided to start using standard-clj. Everyone runs it locally before pushing, CI doesn't check formatting (IMO it's unnecessarily strict to make CI fail because of formatting) It's been working fine but there are couple things that have made us wonder if we should change to something else. Personally, I'm not a fan of the https://github.com/clj-commons/formatter/issues/9#issuecomment-446167649 in standard-clj makes. I'm a happy user of Emacs and aggressive-indent-mode (indentation is kept correct while I type), and since https://github.com/oakmac/standard-clojure-style-js/issues/194 it caused a lot of unnecessary whitespace changes when the code uses argument alignment (according to Rule 3) and then aggressive-indent-mode changes it to fixed two-spaces alignment. We fixed this so that we decided to run a https://github.com/sharetribe/standard-clojure-style-js/tree/format-without-rule-3, which formats without Rule 3, and decided in the team that we'd always use fixed formatting. This almost solves all the indentation issues, except for the namespace (:require ,,,) forms, which are formatted with a single space, unlike any other list in code. Just a couple of weeks ago, we introduced clojure-lsp for our tooling for linting, and what I'd love to try out next is to use it for formatting and see how that works. It uses cljfmt under the hood, which I haven't used before.

2025-09-15T16:56:59.908799Z

@rap1ds Can't you use zprint instead? standard-clj is kind of a no config by philosophy JS formatter for Clojure, can't imagine running a fork of it to "customize" formatting makes sense.

2025-09-15T16:59:19.350579Z

@rap1ds I also don't understand how it creates unnecessary commit diffs? If you said you run standard-clj formatting pre-commit, than it should reset the formatting to it and that Emacs had temporarily changed it shouldn't matter no?

2025-09-15T17:00:48.768099Z

One thing I think zprint is best for the use case of pre-commit is that you can have it fully reformat where others are simply re-indent. Full reformat means even custom newlines, extra spaces and all that are reset to the standard.

2025-09-15T17:09:04.545069Z

@rap1ds Actually I take it back. I'm not a fan of the deviation from Tonsky's simple formatting, and Rule 3 is 💩 🤣 My favorite part about Tonsky's minimal formatting was just going super simple with form indentation. If I am going to go "dumb" indentation I'd like it super simple.

2025-09-16T06:49:33.252769Z

@didibus Sorry, I didn't explain myself clearly. Here's a step-by-step example: 1. Let's say I have a file like this:

(ns rule3-example)

(and true
     true
     true)
2. And I have configured my Emacs to use fixed indentation with the following in my .dir-locals.el
((clojure-mode
  ;; 
  (clojure-indent-style . always-indent)
  (clojure-indent-keyword-style . always-indent)
  (clojure-enable-indent-specs . nil)))
3. Then, I ran standard-clj fix rule3_example.clj. This doesn't change the file, because the indentation is correct, according to Rule 3. 4. Now, given I have aggressive-indent-mode enabled, and I set my cursor after the second true:
(ns rule3-example)

(and true
     true|
     true)
5. And hit Enter and add a false value, this is what I got:
(ns rule3-example)

(and true
  true
  false
  true)
6. If I now run standard-clj fix rule3_example.clj, it doesn't change the indentation because this is correct according to Rule 3. The result is that the indentation has changed from argument alignment to fixed 2 space indentation. This is not standard-clj's fault, this is due to the fact that AFAIK there's no way to configure Emacs and clojure-mode to understand Rule 3. So after taking standard-clj into use and formatting our whole code base with it, we started seeing a lot of PRs with plenty of unnecessary whitespace changes like the one described above. Thus, what we did is we forked the standard-clj to ignore Rule 3 and formatted our codebase with the forked version to get consistent fixed indentation. Then we decided in the team that we're not using argument alignment, only fixed indentation. And now we've been free of unnecessary white-space changes :) I hope this clarifies what I meant! > Can't you use zprint instead? Hmm. I haven't tried it, maybe I should! > standard-clj is kind of a no config by philosophy JS formatter for Clojure, can't imagine running a fork of it to "customize" formatting makes sense. It absolutely does not make any sense and goes fully against the philosophy, I get it :D But we just needed a way to format the codebase with consistent fixed indentation, and since it's a "no config" formatted, there's no --disable-rule3 configuration either, so, what can you do 🤷 But, this was only one-time run. After that, we've used the unforked version for our daily formatting > If you said you run standard-clj formatting pre-commit, than it should reset the formatting to it and that Emacs had temporarily changed it shouldn't matter no? Noup, because or Rule 3, this doesn't happen. Rule 3 allows both styles. I 100% agree with you about Rule 3. I see a lot of value in Tonksy's minimal formatting and I think standard-clj would be much better without Rule 3.

2025-09-16T16:03:38.231699Z

Ya I see, that makes sense. Rule3 itself is responsible for the odd behavior because it dynamically aligns. This is where zprint can be better. Because it'll reconstruct the full alignment if configured to do so. Though it can also be annoying because you have no control, like if you want 3 spaces between something it'll reset it back.

👍 1
2025-09-14T12:44:05.544869Z

at my last job, we used zprint and failed CI on formatting mistakes, but after working there for 2 years, i found it to be mostly a pain without almost any benefits. frequently, we ran into situations where code looked really weird without recourse, and i personally spent a lot of time adjusting the zprint config to make those situations less weird

2025-09-14T12:45:39.374759Z

after all that, i think formatting is relatively unimportant and everyone should change their editor to not format on save.

➕ 2
Gosha 2025-09-14T12:53:16.568439Z

Hmm, that’s definitely a way to go. Thank you for the perspective, Noah!

Steven Lombardi 2025-09-14T15:31:45.771899Z

I'm definitely with Noah on this, with the caveat that at minimum you pick a max line length so devs aren't horizontally scrolling through code (and if you don't want to pick one, flip a coin between 100 and 120).

Chris Oakman 2025-09-14T15:33:09.728869Z

FYI - I know that Colin wants to support Standard Clojure Style in Cursive. And it is a goal of Standard Clojure Style to “meet you where you are”, so I also want to help make this happen and be easy to use.

Steven Lombardi 2025-09-14T15:35:45.826279Z

Where I work, we don't format, and there are several files I have to literally scroll across to read everything, and I'm using an ultra wide display.

Chris Oakman 2025-09-14T15:36:23.953339Z

I started work on a pure Java implementation of Standard Clojure Style, but it is not complete / ready to use yet. https://github.com/oakmac/standard-clojure-style-java

❤️ 1
Omar 2025-09-14T15:39:43.601639Z

I'm in the exact same situation, there's a few small emacs things I've been able to do that seem to match everything intellij does exactly but haven't tested it comprehensively across the entire codebase.

mpenet 2025-09-14T18:47:32.007219Z

clj-fmt is good enough. If you don’t want to be annoying just make ci apply formatting (ex upon merge in main) if it diverges with whatever standard you set, that’s mostly pain free this way.

➕ 1
mpenet 2025-09-14T18:49:38.375649Z

That or just don’t. Basically as soon as it forces people to all converge to a specific style from their side it will start holy wars in my experience.

👍 1
practicalli-johnny 2025-09-14T19:27:34.932029Z

Cljfmt, cljstyle or zprint seem to be the most commonly use tools in my experience. standard-clojure-style is an interesting idea but i don't think its that standard in terms of use (as its very new compared to other tools). I use cljstyle as it has a really nice diff output https://practical.li/clojure/clojure-cli/clojure-style/

cfleming 2025-09-14T19:50:10.576049Z

@gosha I do plan to support standard Clojure style in Cursive, as well as a fully-cljfmt compatible version. But Cursive's built in formatter is already quite configurable - if you have specific cases which are problematic, I can help you configure them in Cursive. If you'd like help with this, probably best to bring it over to #cursive.

❤️ 1
2025-09-14T20:11:09.179879Z

@nbtheduke I don't understand how it was failing CI? Does zprint have a formatting validation mode? Otherwise why didn't you set it up so it auto-formatted pre-commit ? Then it would never fail and PRs would never have formatting diffs

2025-09-14T20:13:33.854859Z

not everyone sets up pre commits, and sometimes suggested changes in github would affect the code in unexpected ways

2025-09-14T20:13:52.772599Z

By the way, in Emacs indentation is handled by ClojureMode itself, no use of zprint or cljfmt. I think this differs from VSCode Calva which uses cljfmt I believe.

2025-09-14T20:15:29.605469Z

What I've always done, is run formatting as part of the build. So not formatting before sending a PR is the same as not having tried to run the tests before submitting a PR. Everyone is expected to run the build command before opening a PR. If not that's the first PR comment you leave for them to fix.

2025-09-14T20:18:44.698949Z

I think you can also set up branch protection rules so a PR can't be merged if it fails certain things, like some "build command".

Steven Lombardi 2025-09-14T15:56:04.019049Z

What exactly is case* and should it be listed in the special forms doc? https://clojure.org/reference/special_forms

Alex Miller (Clojure team) 2025-09-14T16:18:36.226359Z

Several of the special forms have implementation bits that are marked with foo* that are directly understood by the compiler. These should be considered part of the implementation and usually not used directly

Steven Lombardi 2025-09-14T16:23:54.557419Z

Okay so if I'm interpreting Clojure, the guidance would be to implement clojure.core/case myself and not macroexpand it or gain knowledge of case*, does that sound right? Scratch that, I have a better way to do this.

Steven Lombardi 2025-09-14T16:26:04.687369Z

Regardless, this context helps. Thanks to both of you.

amano 2025-09-14T03:30:30.726269Z

This is just a fun impractical idea that's not worth your time. Raku language's rakudo compiler is written in NQP which is a higher-level language designed for compilers. NQP compiles to MoarVM bytecode. In theory, there can be a clojure implementation that targets NQP. Like clojure, raku doesn't yet have tail-call optimization. Raku has a lot more advanced OOP machinery than java. Clojure is a good fit for raku although clojure's OOP capability isn't as detailed as raku's. Raku is a complex language. Since raku is merely a niche language, writing a clojure implementation for NQP is entirely useless, but it's a fun idea regardless. Python is actually important as a platform, Basilisp(clojure on python) should get more time, attention, and resources. According to TIOBE index, python is the king. Basilisp can potentially replace babashka although basilisp's startup time will not be ultra fast. Python is good for GUI applications, system scripts, AI, LLM, data science, etc, ...

amano 2025-09-15T13:53:13.490249Z

https://pypl.github.io/PYPL.html is another popularity index for programming languages. Python is also the king there.

amano 2025-09-15T14:04:39.093979Z

https://www.statista.com/statistics/793840/worldwide-developer-survey-most-used-frameworks/ says python libraries are the most used in the world.

amano 2025-09-15T14:05:07.343659Z

https://www.statista.com/statistics/793628/worldwide-developer-survey-most-used-languages/ says python is the third most used language in the world.

amano 2025-09-15T14:05:50.200939Z

Python is king.

2025-09-15T17:11:51.747789Z

I think you missed my point. That "most used", "most tutorial searched", "most installed" is all moot, and result in things like Scratch, a literal programming language for children, being ranked higher than Rust.

2025-09-15T17:14:51.739239Z

That be like saying that tuktuks are the king of cars because they're the most popular, used, purchased, etc

2025-09-15T19:49:02.000659Z

Sorry, just wanting to also make clear that I think Python is pretty good and clearly dominates certain use-cases. And for that I'm very happy someone is trying to host Clojure on it. What I mean is those ranking all have an underlying criteria, it's not possible to compress languages on a single axis. Python will rank near/at the top in places and others will in other place, all depending on your criteria.

2025-09-15T19:52:58.761819Z

That's why Clojure is pretty awesome, because by having multiple supported runtimes for Clojure you can likely reach every place you'd need to go. And because of that, I hope we continue to see Clojure JVM, Clojure CLR, ClojureScript/squint, Basilisp, ClojureDart, Jank all thrive and continue to be well supported. I even wish we had more, Clojure for Raku for example!

amano 2025-09-16T04:18:55.302239Z

Raku has been useless so far. Python isn't just popular. It is also strong for many use cases including system scripting, GUI development, AI, LLM, data science, and so on. System scripting can be done by almost any linux ninja. Many system applications have been written in python. Desktop GUI development in clojure has not been great so far. AI and LLM are becoming increasingly more important. I considered babashka and other scripting-oriented clojure implementations for system scripting, and none supports libraries. Basilisp libraries will/could be treated as plain python libraries.

amano 2025-09-16T04:23:09.050379Z

If you don't hate writing in python, python is one of the most useful languages. If basilisp integrates with python module system properly, it can replace janet language for my system scripting use case.

amano 2025-09-16T04:26:08.947769Z

Jank can start up faster than basilisp for system scripts, but it seems jank maintainer is not interested in integrating with linux distribution packaging systems.

2025-09-16T04:27:54.839069Z

> Raku has been useless so far 🤣 Wait, but why are you proposing that there could be a Clojure that targets it's runtime? I guess you did say "This is just a fun impractical idea". Is Raku not well supported? Is perl5 still the norm and people haven't moved to Raku?

amano 2025-09-16T04:28:48.233829Z

I wrote a small application in raku and realized it is very complex for my simple needs.... I could have used janet which is vastly simpler than raku. Raku also doesn't have a big ecosystem. Raku doesn't start up fast for system scripts.

👍 1
amano 2025-09-16T04:29:38.437079Z

Janet has been my primary scripting language.

2025-09-16T04:31:07.506359Z

> Python isn't just popular. It is also strong for many use cases including system scripting, GUI development, AI, LLM, data science, and so on. Absolutely, no hate from me. I'm rooting for Basilisp. > I considered babashka and other scripting-oriented clojure implementations for system scripting, and none supports libraries. What do you mean by libraries? Babashka supports libraries, or you mean like linux package managed libraries?

amano 2025-09-16T04:31:32.573609Z

Last time I checked, babashka didn't have installable libraries.

amano 2025-09-16T04:32:38.317559Z

Installable libraries require a proper file system placement.

amano 2025-09-16T04:33:10.178849Z

Python has installable libraries. Raku's module system is more advanced than python's, though.

2025-09-16T04:33:36.774279Z

It does support libraries, it's just not very many people make babashka libraries. Most time people make Clojure libs, and not many also work with bb, but some do. There's a list here: https://babashka.org/toolbox/

amano 2025-09-16T04:34:49.030419Z

Can babashka libraries be installed like python libraries?

amano 2025-09-16T04:35:04.050239Z

System scripts require system libraries. Python libraries can be installed as linux distribution packages. Raku libraries and janet libraries can be installed as linux distribution packages.

amano 2025-09-16T04:38:09.185919Z

I don't want to drag bb.edn around for system scripts.

2025-09-16T04:44:01.301589Z

> Can babashka libraries be installed like python libraries? Hum 🤔 Normally babashka is it's own package manager. So you can do:

(require '[babashka.deps :as deps])
(deps/add-deps '{:deps {some.lib {:mvn/version "822.2.1109.0"}}})
Like inline inside your script to include libs. Or similarly you can define them in bb.edn Which isn't what you want I assume

amano 2025-09-16T04:45:23.517499Z

This is why janet is my system scripting queen.

2025-09-16T04:50:37.664699Z

> I don't want to drag bb.edn around for system scripts. Well you don't have too. For example you can just intall bb and then run:

#! /usr/bin/env bb

(require '[babashka.deps :as deps])
(deps/add-deps '{:deps {coldnew/left-pad {:mvn/version "1.0.0"}}})

(require '[coldnew.left-pad :refer [leftpad]])
(println (leftpad "Hello, world!" 20 "-"))
The script will dynamically install the dependencies it needs as it runs (that's what the deps/add-deps does).

amano 2025-09-16T04:51:06.528939Z

maven? Does it pull JVM dependencies?

2025-09-16T04:51:22.681839Z

This is what the above script did on my machine:

> ./fun.bb
Downloading: coldnew/left-pad/1.0.0/left-pad-1.0.0.pom from clojars
Downloading: org/clojure/clojurescript/1.8.34/clojurescript-1.8.34.pom from central
Downloading: org/clojure/data.json/0.2.6/data.json-0.2.6.pom from central
Downloading: org/clojure/tools.reader/1.0.0-alpha3/tools.reader-1.0.0-alpha3.pom from central
Downloading: org/clojure/data.json/0.2.6/data.json-0.2.6.jar from central
Downloading: org/clojure/tools.reader/1.0.0-alpha3/tools.reader-1.0.0-alpha3.jar from central
Downloading: org/clojure/clojurescript/1.8.34/clojurescript-1.8.34.jar from central
Downloading: coldnew/left-pad/1.0.0/left-pad-1.0.0.jar from clojars
-------Hello, world!
And on subsequent runs it just does:
> ./fun.bb
-------Hello, world!

2025-09-16T04:52:37.154919Z

It can pull from maven, clojars, git, or a folder on your machine. The code has to be "babashka compatible". So either Java or Clojure libraries that use only the Java classes and Clojure namespaces included in babashka itself. Or some actual babashka library written in babashka.

2025-09-16T04:53:13.622709Z

You don't need a JVM though, it's all running inside Babashka, it's just Babashka can run a small subset of Java

amano 2025-09-16T04:53:32.872669Z

I also write small system utilities in system scripting languages. That's not going to fly.

2025-09-16T04:54:22.166389Z

Well, maybe I take it back on java libs, I'm not so sure about pure java, but Clojure one ya. Like this is the lib I used in the above script: https://github.com/coldnew/left-pad.clj

amano 2025-09-16T04:54:39.943069Z

I want to start with a script and deliver it as a proper system utility when I want to.

amano 2025-09-16T04:55:00.832099Z

That means python, janet, raku, guile scheme, and so on.

amano 2025-09-16T04:55:32.585479Z

Jank maintainer doesn't want to target system scripting, so jank is no good for me personally.

amano 2025-09-16T04:55:47.230049Z

System scripting entails system libraries.

2025-09-16T04:58:11.612069Z

Ya, that's fair. I think in bb you can bundle your whole script either as an uberscript or an uberjar (but like a bb one). So it would be bundled with it's dependencies. But that does mean if two script use the same lib/version it's duplicated in each, unlike with proper linux package management where they'd point to the same one.

amano 2025-09-16T04:59:07.782029Z

babashka is a project scripting language.

2025-09-16T04:59:27.194009Z

Whenever I used Python, I always used pip and conda and such, and never my distro package manager. But I know my distro does include python utilities that pull down python libs, so at least it has that option.

amano 2025-09-16T05:00:02.562619Z

I don't want multiple package managers. I manage everything with linux distribution package managers.

amano 2025-09-16T05:03:10.627299Z

To me, basilisp is the only hope for clojure as a system scripting language.

2025-09-16T05:03:59.854799Z

Question, in a linux package manager, how does the code knows where to look for dependencies? Like how does python know that libx is in /opt/lib/... (hypothethical)

amano 2025-09-16T05:04:19.621449Z

• Environment variables • Default directories that are determined during build.

2025-09-16T05:04:49.810289Z

Hum...

amano 2025-09-16T05:05:37.197849Z

The default system module directories are determined as compilation options.

amano 2025-09-16T05:05:47.025109Z

You can add more with environment variables.

Shantanu Kumar 2025-09-16T05:08:37.555769Z

@amano.kenji I am guessing, (GNU) Guile could be closer than alternatives to your requirement. It is not Clojure, but it's a Lisp.

amano 2025-09-16T05:08:57.424879Z

I might learn guile if I want to use gnu guix system, but learning guile will take some time which I don't want to invest unless I really want or need guile and guix system for some reasons.

amano 2025-09-16T05:09:46.600739Z

For now, I just write in janet language which is simple and which I already know.

👍🏽 1
2025-09-16T05:10:33.825109Z

I think maybe then you could package bb libs in the system package manager. It would be annoying since no one does so. But you could do:

#! /usr/bin/env bb

(require '[babashka.deps :as deps])
(deps/add-deps '{:deps {coldnew/left-pad {:local/root (str (System/getenv "MY_LIBS") "/left-pad"}}})

(require '[coldnew.left-pad :refer [leftpad]])
(println (leftpad "Hello, world!" 20 "-"))

2025-09-16T05:15:40.657649Z

Hum... I think the issue, is that if a lib you depend on depends on other libs, they won't be doing this trick, and so they'd have their libs pull down to your machine by babashka itself, bypassing your package manager again

amano 2025-09-16T05:16:32.784379Z

Hence, basilisp as the only hope for any chance of using clojure as a system scripting language. If basilisp integrates with python module system, it will be a system scripting language. Right now, the integration doesn't exist.

😢 1
1
2025-09-16T05:22:58.265909Z

ClojureScript can use node_modules deps no? I guess that's also an option, but ya, it's a lot of hassle to setup just for scripts.

amano 2025-09-16T05:23:23.173449Z

node_modules is a nightmare for most linux distributions.

amano 2025-09-16T05:25:44.708009Z

What I realize again is that nothing is perfect, and I should accept mediocrity. I should accept thing as they are now. I accept that clojure is not a system scripting language, yet. I shall stick to janet for the foreseeable future.

2025-09-16T05:28:40.184959Z

Was just reading a bit on why node_modules is more annoying than venv. Seems because it duplicates everything and maybe pulls in binaries not always built for the local machine?

2025-09-16T05:30:10.210539Z

> I accept that clojure is not a system scripting language, yet. Ya, admittedly, it's not friendly to being included in a distro and used for distro-level packaging. Is Basilisp thinking on using pip or maven/git ?

2025-09-16T05:34:11.425489Z

Doc says: > PYTHONPATH, sys.path, and Finding Basilisp Namespaces > Basilisp uses the PYTHONPATH environment variable and sys.path to determine where to look for Basilisp code when requiring namespaces. This is roughly analogous to the Java classpath in Clojure. These values may be set manually, but are more often configured by some project management tool such as Poetry or defined in your Python virtualenv. These values may also be set via https://basilisp.readthedocs.io/en/latest/cli.html#cli arguments. Wouldn't that make it viable for system scripting?

2025-09-16T05:43:33.799849Z

Ah I see, it doesn't yet have dependency management tooling so it's a question mark

amano 2025-09-16T12:11:29.243329Z

Tight integration with python module system will solve that issue. I know how it should be done, but I don't have time to do it myself. I'd rather pay the maintainer to work on it full-time if I have money. Money buys other people's time. Become rich.

Shantanu Kumar 2025-09-14T07:22:25.822279Z

@didibus AFAIK: Raku is Perl6, Perl is Perl5 - https://www.infoworld.com/article/4053221/perl-programming-language-rises-again-tiobe.html

2025-09-14T04:48:46.243259Z

Isn't raku perl?

➕ 1
2025-09-14T04:58:29.099809Z

Also, I'm super down for Basilisp, I love Clojure having many viable runtimes like that, but I'm not sure TIOBE index is a good representation. I mean scratch is number 16 and it's literally a programming language for babies.