Respecting OCaml programming guidelines
Link: https://ocaml.org/learn/tutorials/guidelines.html
-
[x] Delimiters:
- [x] A space should always follow a delimiter symbol
- Ofmt: by default, option
field-spaceallows to add a space at the left of a delimiter symbol (disabled by default)
- Ofmt: by default, option
- [x] Spaces should surround operator symbols.
- Ofmt: by default
- [x] A space should always follow a delimiter symbol
-
[x] Tuples:
- [x] A tuple is parenthesized and the commas therein (delimiters) are each followed by a space.
- Ofmt: option
parens-tupletakes care of this,alwaysby default
- Ofmt: option
- [x] Not adding parentheses can be accepted for pairs.
- Ofmt: new option
parens-tuple-patterns(#498)
- Ofmt: new option
- [x] Also accepted when matching on several values simultaneously.
- Ofmt: new option
parens-tuple-patterns(#498)
- Ofmt: new option
- [x] A tuple is parenthesized and the commas therein (delimiters) are each followed by a space.
-
[x] Lists:
- [x] Write
x :: lwith spaces around the::(since::is an infix operator, hence surrounded by spaces)- Ofmt: by default
- [x] Write
[1; 2; 3](since;is a delimiter, hence followed by a space).- Ofmt: by default
- [x] Write
-
[x] Symbol operators: Be careful to keep operator symbols well separated by spaces: not only will your formulas be more readable, but you will avoid confusion with multi-character operators. (Obvious exceptions to this rule: the symbols
!and.are not separated from their arguments.) Example: writex + 1orx + !y.- Ofmt: by default
-
[x] Long character strings: Indent long character strings with the convention in force at that line plus an indication of string continuation at the end of each line (a
\character at the end of the line that omits white spaces on the beginning of next line).- Ofmt: option
break-string-literalstakes care of this, wrapping by default, can be disabled
- Ofmt: option
-
[ ] Indentation: The change in indentation between successive lines of the program is generally 1 or 2 spaces. Pick an amount to indent and stick with it throughout the program.
- Ofmt: Not always true (issue #505)
-
[ ] Indentation of global
letbindings:- [x] regular indentation of 1 or 2 spaces
- Ofmt: 2 by default, could be an option
- [ ] the body can be left-justified in the case of pattern-matching
- Ofmt: could be an option: issue #265
- [ ] the body can be justified just under the name of the defined function
- Ofmt: Could be an option (equivalent to option
baseofocp-indent(issue #476)
- Ofmt: Could be an option (equivalent to option
- [x] regular indentation of 1 or 2 spaces
-
[x] Indentation of
let ... inbindings:- [x] The expression following a definition introduced by
letis indented to the same level as the keywordlet, and the keywordinwhich introduces it is written at the end of the line. In the case of a series ofletdefinitions, the preceding rule implies that these definitions should be placed at the same indentation level.- Ofmt: by default
- [x] Variation: some write the keyword
inalone on one line to set apart the final expression of the computation.- Ofmt: by default, in case of multi-line, could be an option (issue #506)
- [x] The expression following a definition introduced by
-
[ ] Indentation of
if ... then ... else ...(multiple branches):- [ ] Write conditions with multiple branches at the same level of indentation. If the sizes of the conditions and the expressions allow, the keywordd
elsecan end the line.- Ofmt: not especially readable but could be an option (issue #504)
- [ ] If expressions in the branches of multiple conditions have to be enclosed (when they include statements for instance), the blocks start with
then beginand end withend else.- Ofmt: done with parens by default, could be an option to use
begin/endinstead
- Ofmt: done with parens by default, could be an option to use
- [x] Some suggest another method for multiple conditionals, starting each line by the keyword
else.- Ofmt: by default the option
if-then-elseis set tocompactbutkeyword-firstputs keywords first
- Ofmt: by default the option
- [ ] Write conditions with multiple branches at the same level of indentation. If the sizes of the conditions and the expressions allow, the keywordd
-
[x] Indentation of
if ... then ... else ...(single branches):- [x] In the case of delimiting the branches of a conditional, several styles are used:
(at the end of the line, orbeginat the beginning of the line.- Ofmt: by default,
(at the end of the line, could be an option to usebegininstead
- Ofmt: by default,
- [x] If
cond,e1ande2are small, simply write them on one line.- Ofmt:
if-then-else=compactby default
- Ofmt:
- [x] In the case of delimiting the branches of a conditional, several styles are used:
-
[x] Pattern-matching:
- [x] All the pattern-matching clauses are introduced by a vertical bar, including the first one.
- Ofmt: by default
break-cases=fitbut can be set toall
- Ofmt: by default
- [x] Align all the pattern-matching clauses at the level of the vertical bar which begins each clause, including the first one.
- Ofmt: by default
- [x] If an expression in a clause is too large to fit on the line, you must break the line immediately after the arrow of the corresponding clause. Then indent normally, starting from the beginning of the pattern of the clause.
- Ofmt: by default
- [x] Arrows of pattern matching clauses should not be aligned.
- Not aligned at the moment but could be an option in the future (https://github.com/ocaml-ppx/ocamlformat/issues/360)
- [x] All the pattern-matching clauses are introduced by a vertical bar, including the first one.
-
[ ]
match/try:- [x] For a
matchor atryalign the clauses with the beginning of the construct- Ofmt: by default
- [ ] Put the keyword
withat the end of the line. If the preceding expression extends beyond one line, putwithon a line by itself.- Ofmt: if the expressions following
withfit on the line,withis not on a line by itself, could be an option (issue #507)
- Ofmt: if the expressions following
- [x] For a
-
[x] Expressions inside clauses:
- [x] If the expression on the right of the pattern matching arrow is too large, cut the line after the arrow.
- Ofmt: by default
- [x] Alternatively, the line can be cut after the arrow no matter the length of the pattern.
- Ofmt: option
break-cases=all(but also breaks or-cases)
- Ofmt: option
- [x] Careful alignment of the arrows of a pattern matching is considered bad practice.
- Ofmt: not done
- [x] If the expression on the right of the pattern matching arrow is too large, cut the line after the arrow.
-
[x] Pattern matching in anonymous functions: Similarly to match or try, pattern matching of anonymous functions, starting by
function, are indented with respect to thefunctionkeyword.- Ofmt: by default
-
[x] Pattern matching in named functions:
- [x] Pattern-matching in functions defined by
letorlet recgives rise to several reasonable styles which obey the preceding rules for pattern matching (the one for anonymous functions being evidently excepted).- Ofmt: by default
- [x] Don't indent under the keyword
matchorfunctionwhich has previously been pushed to the right, indent the line under theletkeyword.- Ofmt: by default
- [x] Pattern-matching in functions defined by
-
[x] Indentation of the function's name:
- [x] You must indent the expressions with respect to the name of the function (1 or 2 spaces according to the chosen convention).
- Ofmt: by default
- [x] Write small arguments on the same line, and change lines at the start of an argument.
- Ofmt: by default
- [x] You must indent the expressions with respect to the name of the function (1 or 2 spaces according to the chosen convention).
-
[x] Indentation of the operations: When an operator takes complex arguments, or in the presence of multiple calls to the same operator, start the next the line with the operator, and don't indent the rest of the operation.
- Ofmt: by default
-
[x] How to delimit constructs in programs: When it is necessary to delimit syntactic constructs in programs, use as delimiters the keywords
beginandendrather than parentheses. However using parentheses is acceptable if you do it in a consistent, that is, systematic, way. This explicit delimiting of constructs essentially concerns pattern-matching constructs or sequences embedded withinif then elseconstructs.- Ofmt: parentheses are used instead of
begin/end, it could be an option (issue #491)
- Ofmt: parentheses are used instead of
-
[x] match construct in a match construct: When a
match ... withortry ... withconstruct appears in a pattern-matching clause, it is absolutely necessary to delimit this embedded construct (otherwise subsequent clauses of the enclosing pattern-matching construct will automatically be associated with the enclosed pattern-matching construct).- Ofmt: by default
-
[x] Sequences inside branches of if: A sequence which appears in the
thenorelsepart of a conditional must be delimited.- Ofmt: by default (with parentheses)
-
[x] Opening modules: Avoid
opendirectives, using instead the qualified identifier notation.- Ofmt: by default the
let-openpreserves the initial style, but theshortvalue forces the qualified identifier notation
- Ofmt: by default the
One thing that needs to be kept in mind here is that these guidelines were written in the context of "guidelines" for human authors, where bad/confusing corner cases could be avoided by selectively not following the guidelines where necessary. Options to implement rules that follow these guidelines will still need to be evaluated on a lot of code to see what corner cases come up.
Another thing to bear in mind is that these guidelines are not set in stone. If there is some corner case that greatly simplifies ocamlformat (or just a genuine departure from them due to an evolution of the language), then we can submit recommended changes upstream to ocaml.org.
Agreed.
Also, there are cases where formatting code in a particular way is very onerous and tedious to do manually, but can still be more clearly legible / informative of how the parser interprets the code. One of the benefits of an auto-formatter is the ability to depart from guidelines for manual formatting in such cases.
Could we get examples and counter-examples for each bullet point? Please note that I landed here from https://discuss.ocaml.org/t/ocamlformat-and-the-ocaml-org-coding-guidelines/2922 and I'm only here to get a quick glance.
For example, in the List section:
do:
[1; 2; 3]
don't:
[ 1; 2; 3 ]
[1;2;3]
So if there's a weird formatting rule, it will stand out. Thanks!