clojure-style-guide
clojure-style-guide copied to clipboard
Single-space indentation when no arguments on same line as function name
We recently had discussion about this rule at work. We have two Emacs user, single Vim user and many Cursive users and all but Emacs users found this rule strange. Maybe it's because we don't have lisp background, but then I think good part of Clojure community nowadays doesn't have Lisp background.
To solve this at work we could just fork the style guide and decide on our own rule, but I wonder if this rule is worth to discuss on community level. I would like to hear more opinions on this rule to see if the style guide needs some changes? At least would be good to mention the rationale for rule as it doesn't seem to be that obvious :)
;; good
(filter
even?
(range 1 10))
;; bad?
(filter
even?
(range 1 10))
Reasons for single space indentation:
- Used in every Lisp for decades
- Used by Emacs (clojure-mode)
- Plenty of code using this rule?
Reasons against (or evidence that this is not obvious):
- Not used by Vim (vim-clojure-static), Cursive
- There's plenty of code not adhering to this rule (ring, just searched for one example on a popular lib, there is probably opposite examples too)
- Books mentioned in style guide don't use the rule 1 (they break other rules too, so not that great example)
- Simpler rules, no need for have special cases for macros with body forms which are indented with 2 spaces (so it's complex to set up this rule when editor doesn't have built-in support)
- Clojure Programmer, page 140:

I've been baffled by one-space indents since they were introduced to the Style Guide and to clojure-mode. As for "Used in every Lisp for decades", all of the following use 2-space indents consistently: SICP, Graham's ANSI Common Lisp, Graham's On Lisp, Norvig's PAIP, and Hoyt's Let Over Lambda, Colin Jones's Mastering Clojure Macros, Fogus and Chouser's Joy of Clojure, etc., etc.
At the very least I think the style guide should be agnostic r.e. 1- vs 2-space indents, and clojure-mode should allow either as the default, configurable (maybe it does already). Cheers!
Cursive has a setting to enable this, to allow Cursive users in mixed teams to match the Emacs indentation if necessary. It's off by default, though, and I think two spaces should be the default for all editors in Clojure.
The arguments about other lisps are generally not valid since other lisps use lists much more than Clojure does, in particular for data - lists in Clojure are almost exclusively code forms and should be indented with two spaces.
vim-clojure-static supports both styles as well. 2 spaces is the default.
https://github.com/guns/vim-clojure-static#gclojure_align_subforms
I'm not against changing this, if it's actually used in practice, but I'm kinda wondering - do you still align arguments at all using using 2-space indent or not. Probably you shouldn't, otherwise this style doesn't make sense.
Btw, Graham is known for his unidiomatic Lisp style. :-)
@bbakersmith Yes, but one needs to then manually set up all special cases for macros which are intended with 2 spaces.
@bbatsov What do you mean
do you still align arguments at all using using 2-space indent or not
I vertically align arguments when I have arguments on the same line as function name. I indent arguments with 2 spaces when there are no arguments on the same line as function name.
This case is not super common, but it does come up now and then. Some common cases where I have found this happens is with functions calling filter, reduce, postwalk etc. with unnamed functions. Like the example from Clojure Programmer.
FWIW, re: "other Lisps", Racket defaults to one-space. I suspect the reason is that simply this would be the default in any list form. '(1 3 4) would be done with one-space as well in any Lisp. Clojure has never struck me as particularly committed to following the idioms of other Lisps however, so that needn't necessarily be the assumption in any case. For my part I rarely if ever have done the thing in question, so I'm not sure I have an opinion either way.
@Deraen I wondered if you'd write:
(filter even?
(range 1 10))
or
(filter even?
(range 1 10))
On the subject of books - PCL using single-space ident.
@bbatsov The second one.
That's what I thought and that's why I think the 1-space rule is better - you always align the elements, instead of coming up with a superficial need not to align them in some case.
Just my 2 cents. I know some people are in favor of 2-spaces everywhere, but I don't think we can ever reach a consensus. It just boils down to preference now.
To me, a function form is just a list where the head happens to be a function.
In other lisps (LISP-2) it even makes a little sense to make the arguments look different from the function; because the arguments are resolved by value, while the symbol at the head is resolved by its function-binding.
But Clojure is a LISP-1, so there's no difference between the head and the other elements (i.e., the arguments and the function). The head of the list can be a symbol (which is resolved by value just like all the args), or it can be a map, a vector, a keyword, etc.
Macros get 2-space indent because they are different. If a var is a macro you can't take its value, so it _is_ special. A macro form is not just a list with a macro at the head, and the form you write is not the form that reaches the interpreter.
@bbatsov It's not a superficial need not to align them - the parameters are still aligned under either scheme, the question is whether they're aligned with a one space or two space indent.
A once space indent is actually not really indented at all, since it means that the params are aligned with the head symbol. Clojure would be the only language I know of where a function invocation run onto various lines has no indentation at all to indicate that that's what the form is.
I suspect this is just a taste thing, and I have no plans to change Cursive's default. I think that CIDER should provide a setting for this though, if it doesn't already, so that whichever a team prefers can be respected. I also think the style guide should not prescribe one or the other, although it's clearly advertised as bbatsov's opinionated guide.
Well this is a repo of opinions about things that don't really matter :stuck_out_tongue: That said, I'll chime in for the 1/2spc indent rather than the width of op indent. It's a question of priorities, but in my experience the most common cases of breaking arguments onto the next line are nested sequence operations (which arguably should be ->> or let bound or something) which can get really hairy. When I choose to break arguments to the next line, I'm usually doing so to preserve some amount of readability and minimize per-term indent width. Indenting to invocation target width by default doesn't serve that goal as well as indenting to 1/2spc.
I suspect this is just a taste thing, and I have no plans to change Cursive's default. I think that CIDER should provide a setting for this though, if it doesn't already, so that whichever a team prefers can be respected. I also think the style guide should not prescribe one or the other, although it's clearly advertised as bbatsov's opinionated guide.
I'm fine with supporting the other indentation scheme in clojure-mode (if this doesn't require significant changes), but first I need to understand exactly what it is. A few examples covering the most common use-cases would be useful.
@bbatsov It's not a superficial need not to align them - the parameters are still aligned under either scheme, the question is whether they're aligned with a one space or two space indent.
A list will clearly be misaligned if the second item is not aligned to the first one. :-) And what about vectors - or this special alignment applies only to lists? As I said - if people want this, probably we should support it somehow on Emacs, but I need to understand what exactly do they want.
I can also assure that my personal coding preferences are not totally aligned with this guide - I'm really aiming for some community input here and consider myself just a mere editor.
I'm fine with supporting the other indentation scheme in clojure-mode (if this doesn't require significant changes), but first I need to understand exactly what it is.
Sure, here's how this works in Cursive for standard functions (which is the only case that this rule applies to). Everything after the head form is a parameter, and they're always aligned. So here are some examples:
(myfn a b c) ; duh
(myfn a
b
c)
(myfn a b
c) ; ugly, but params are still aligned
This rule only kicks in when none of the parameters are on the same line as the head form:
(myfn
a b c)
(myfn ; This is really the only case you see in practice, and the one people care about
a
b
c)
(myfn ; This is probably a firing-squad offence, but this is how it would look
a b
c)
A list will clearly be misaligned if the second item is not aligned to the first one. :-)
The parameters are all aligned.
And what about vectors - or this special alignment applies only to lists?
Functions (and macros, if they have no special configuration) are formatted like this. Vectors are data, and are not formatted in this way. This, I think, is why most other lisps use one-space formatting, because lists are frequently data there. In Clojure, they're nearly always code.
OK. This should be relatively easy to implement. I've filed a ticket to track the task here https://github.com/clojure-emacs/clojure-mode/issues/362
clojure-mode now supports this indentation style, so we can get back to discussing the merits of the two approaches (btw, clojure-mode actually supports 3 indentations style - always indenting arguments with 2 spaces, instead of aligning them). I wonder if someone is willing to do a bit of research which is the most common pattern in the wild...
Great!
:align-arguments is what Cursive and Fireplace use so I think that is what most users of these editors use. Still waiting for State of Clojure Community 2015 survey results but I suspect that Cursive has gained lots of users.
Probably you're right. And while I'm open to updating the guide in some way, as long as I'm the steward of clojure-mode the default there will never be changed.
I have some code laying around that parses clojure repositories. I could adapt it to analyse indentation.
That'd be great. In general it'd be nice to gain more insight about what people are actually using in the wild - e.g. the recent debate about defn- vs ^:private.
Cursive also supports these three styles. Thanks for adding this!
You're welcome. Guess only vim supports just one now. :-)
align-arguments is the default but always-align is an option: https://github.com/guns/vim-clojure-static#gclojure_align_subforms (but breaks macro indentation unless user creates list of macros to indent properly).
Hmm, I thought there was another major-mode for vim. Guess I was mistaken.
Btw, how should lists starting with literals like numbers and keywords be indented in vim/cursive style? Same as functions?
(1
2
3)
(:require
ala
bala)
In Cursive, I just indent them the same way as all other lists. The first is obviously data, but the second might be an invocation, there's really no way to tell.
Got it. Thanks!
The description of this rule may be ambiguous, since we have reader conditional.
#?(:clj
abc
:cljs
abc)
In this case, it's not the "Single-space".
@DogLooksGood In my opinion reader conditionals are a special case, and are always indented like in your snippet.
(Vim-clojure-static doesn't currently have this rule, but I have PR open to implement it.)
@Deraen Not only reader conditionals, but also
#(or
%1
%2)
and
_#(
this is not important
)
I think the word should be align with the function/macro instead of single space.
I do indent code inside anonymous function literals the same as normally:
(or
1
2)
#(or
1
2)
Kind of related to https://tonsky.me/blog/clojurefmt/