Skip to content

Clojure should have a simple code formatter #1

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
oakmac opened this issue May 31, 2023 · 7 comments
Open

Clojure should have a simple code formatter #1

oakmac opened this issue May 31, 2023 · 7 comments
Assignees

Comments

@oakmac
Copy link
Owner

oakmac commented May 31, 2023

Clojure is a popular functional language that targets host platforms on the JVM, JavaScript, and more (1, 2, etc). Clojure is a Lisp and shares in the rich history of that unique syntax.

As of 2023, there is not a standard way to format Clojure code. The Clojure Style Guide is a popular document, but it has many rules and requires program runtime information to be applied 100% correctly. There are some excellent libraries to automatically format Clojure code such cljfmt and zprint. While many engineering teams use these tools successfully to consistently format their codebase, they have some downsides. Both projects provide many config options and are not easy to run in all environments and IDEs.

In 2018, there was a request to create a standard Clojure formatter in the spirit of gofmt (the standard code formatter for the go programming language). This resulted in much valuable discussion, but ultimately no tool was created with large community adoption.

The purpose of this project is to provide a "no config, runs everywhere, follows simple rules" formatter for Clojure code. It is heavily inspired by the Better Clojure Formatting article written by Niki Tonsky.

This project is written in JavaScript - which has wide platform reach - with the intention of creating a small, language-portable implementation that can be cloned to multiple languages with ease. This approach is inspired by how the Parinfer core algorithm is ported to multiple languages (parinfer.py, parinfer-lua, etc).

@oakmac oakmac self-assigned this May 31, 2023
@dgr
Copy link

dgr commented Aug 28, 2024

I’m not sure I understand the rationale for using JavaScript. While parinfer is a general capability for sexp-based languages, a formatter for Clojure(Script) is Clojure-specific, no? So, why not write it in something like Babashka? Or am I totally missing something. To be clear, the overall intent of the project is interesting (though it may be difficult to get everyone to agree on the One True Format), but the use of JavaScript strikes me as a very odd choice.

@didibus
Copy link

didibus commented Aug 30, 2024

Don't understand why it's in JavaScript. Make it cljc seems much better.

I write Clojure code, I don't install npm on my machines, so I won't be able to use this formatter.

Ideally, you make a cljc that can be compiled with GraalVM native compilation, runs in Clojure and ClojureScript and runs under Babashka. This seems like it would have the most reach without requiring people to install non-clojure runtime for formatting Clojure code.

@oakmac
Copy link
Owner Author

oakmac commented Sep 4, 2024

Thank you for the comments @dgr @didibus

choice of JavaScript

My goal for Standard Clojure Style is to create a "fast, no config, works everywhere, language-neutral algorithm for formatting Clojure code". I choose JavaScript as the first implementation language due to familiarity with the ecosystem as well as JavaScript having wide reach on many platforms (Node.js, web, VS Code extensions, etc).

Once the implementation is stable, I plan to port the library line-for-line to several languages in order to "meet the user where they are at". I would like to see a Python, Lua, Kotlin, possibly Zig implementation in the future. This is currently how Parinfer works (see parinfer.js, parinfer.py, parinfer-lua, and others) and I am taking the same approach here.

Standard Clojure Style should be everywhere

From Niki Tonsky's better clojure formatting:

Clojurefmt has to be everywhere. Any editor. Any language. Stand-alone tool. Browser. Libraries. If we want everyone to use it, we should give it to everyone. Nothing sucks more than “it works in Emacs but everybody else needs to figure out a way to run it”. Or “it’s written in Clojure so it takes 10 seconds to start up, practically unusable”.

This constraint requires a certain simplicity of the standard. Fewer rules, low language specificity, simpler conditions. So it could be reimplemented everywhere.

I basically agree with this analysis: I want a small tool that works everywhere and uses simple rules.

The coding style in lib/standard-clojure-style.js is intentionally very simple in order to make porting to different languages easy. The library has zero dependencies and contains very little JavaScript-specific code. As of this comment (September 2024), the core formatting library is ~3,200 lines of code and can parse and format most Clojure source files in a few milliseconds.

using existing Clojure libraries / tools

Ideally, you make a cljc that can be compiled with GraalVM native compilation, runs in Clojure and ClojureScript and runs under Babashka.

This is basically how the zprint project works. My goal for Standard Clojure Style is not to replace zprint, but to offer an alternative for teams who may want a less aggressive formatter that works consistently everywhere.

@didibus
Copy link

didibus commented Sep 4, 2024

Thank you for explaining the rational. I still find it odd that the reference implementation wouldn't be in Clojure, but it's probably easier to port an imperative algorithm to other imperative languages, and Clojure's implementation might not be very imperative friendly.

Why you didn't mention porting it to Clojure at all though? Is that not planned?

Yes it should be available everywhere, and you can do that with Clojure. A Clojure implementation will let you make it available to browsers, node.js, VSCode, IntelliJ, standalone binary, mobile, from the REPL, etc. You'd only need to port it to Emacs Lisp to make it natively available from Emacs (or you could have a plugin that uses the binary).

Zprint does offer that, but it's not that simple, zero config, formatter.

Anyways, that's just my 2 cents. As a user, I probably won't use it if it doesn't have proper Clojure implementation, because I like having Clojure all the way down. I don't mind ports to other languages, I think that's great as well, but if you were to consider a Clojure implementation as well that would revive my interest.

Best wishes for the project non the less! I think it's a great idea, and I don't want to burst your drive, I really hope it takes hold!

@imrekoszo
Copy link
Contributor

Would it make sense to add a concise definition of the boundaries of this project, i.e. how far does it go, where does it draw the line between enforcing consistency and allowing semantic expression, and the reasoning behind the decision(s)? Taking (a rough and buggy interpretation of) Prettier as an example (full rationale here):

  • Prettier enforces consistency of whitespace between tokens
  • It allows single-line expressions but if it doesn't fit the line, then every token (or kvp) goes onto its own line (either all fn args are on the same line or they all have their own lines)
    • It similarly formats nested function calls
    • Which effectively prevents panhandles (as coined by a good friend of mine) from forming
  • It enforces a single space between tokens on the same line
  • But it doesn't reorder object properties for example, even if that wouldn't change the meaning of the code

The formatting rules and things it doesn't do sections describe some of these but I feel a holistic description of what and why this tool considers standard and what it leaves up to nit comments in PRs would be helpful in managing expectations.

@a13
Copy link

a13 commented Nov 10, 2024

My goal for Standard Clojure Style is to create a "fast, no config, works everywhere, language-neutral algorithm for formatting Clojure code".

Sure, but why do you need a Clojure code formatter if you don't have some port of Clojure installed?

@kimo-k
Copy link

kimo-k commented Nov 12, 2024

Why do you need a Clojure code formatter if you don't have some port of Clojure installed?

I suspect there could be more viable answers to this than we all expect.

This talk puts the javascript question into some context: https://youtu.be/oNhqqiKuUmw?t=397
I find it pretty convincing. Yes, js is ugly, but that's a non-issue. On the other hand, the velocity we could gain from standard-clojure-style's distribution model sounds real. That's why I'm keeping an eye on this project.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants