PureScript style guide
August 5, 2016 ยท View on GitHub
This is a style guide for the language PureScript based on conventions used in its core libraries. A style guide is a set of conventions about how to write code for a project. It is easier to read a project's source code when it all has a consistent style. It is easier to contribute code to a project when there is no question as to how something should be written.
This style guide is licensed under the Creative Commons Zero license.
Table of contents
- 1. General formatting
- 1.1. Lines should be no longer than 80 characters
- 1.2. Do not use tabs
- 1.3. Indent with 2 spaces
- 1.4. Separate top-level definitions with a blank line
- 1.5. Surround operators with spaces
- 1.6. No space after a lambda
- 1.7. Lines must not have trailing spaces
- 1.8. Place each data type constructor on its own line
- 1.9. Don't align code
- 1.10. Place each element in a long array on its own line
- 1.11. Precede items in arrays, records, and import lists with commas
- 1.12. Format export lists like arrays
- 1.13. Doubly indent nested record or array literals
- 1.14. Prefer case expressions over equational pattern matching
- 2. Naming
- 3. Imports
- 4. Comments
- 5. Miscellaneous
1. General formatting
1.1. Lines should be no longer than 80 characters
Lines should not be longer than 80 characters.
Rationale
Long lines hinder readability by making the eye scan too far across the screen or page. The column at which a line should be broken, however, is somewhat arbitrary and chosen for historical reasons.
Exceptions
URLs must not be split over multiple lines.
1.2. Do not use tabs
Source files must not contain tab characters; use spaces for indentation.
Rationale
Indentation has syntactic meaning in PureScript, meaning that tabs must be interpreted as having a fixed width. However, editors can display tabs as any width. Thus, code that looks visually correct can be syntactically incorrect, especially if tabs have been mixed with spaces.
1.3. Indent with 2 spaces
Code blocks should be indented with 2 spaces.
Exceptions
- Nested record and array literals should be indented 4 spaces.
letbindings should be indented 4 spaces as well.
1.4. Separate top-level definitions with a blank line
- There should be one blank line between top-level definitions.
- Do not place blank lines between type signatures and function definitions.
- One blank line may be placed between functions in type class instance declarations if the functions bodies are large.
1.5. Surround operators with spaces
Binary operators should be surrounded with a single space on either side.
1.6. No space after a lambda
Do not insert a space after the lambda symbol (backslash or \).
1.7. Lines must not have trailing spaces
Spaces must not appear at the end of any line of source code.
1.8. Place each data type constructor on its own line
Data types with multiple constructors should have each constructor placed on its own line.
Examples
data Color
= Red
| Blue
| Green
1.9. Don't align code
Do not align code. Types within field declarations must not be aligned with one another; instead, they should be flush against the field name. Additionally, patterns and arrows in pattern match cases should not be aligned with one another.
Rationale
Alignment makes it harder when updating code. For instance, if a new record field is added that is longer than the rest, the rest of the fields must be retabulated if the formatting is to be maintained. Doing so is even worse, however, because the resulting "diff" of the edit will indicate that several lines have changed, even though they were only reformatted.
Examples
data Rectangle = Rectangle
{ x :: Int
, y :: Int
, width :: Int
, height :: Int
}
1.10. Place each element in a long array on its own line
Each successive element in a long array should be placed on its own line. If an array literal doesn't fit on a single line then it should be considered "long".
Rationale
This makes for more readable diffs when having to alter a long array.
Examples
elements =
[ div
, h1
, p
, em
, span
, body
]
1.11. Precede items in arrays, records, and import lists with commas
Each successive element in a long array, record, or import list should be preceded by a comma and a space.
Rationale
PureScript does not support trailing commas, thus preceding an element with a comma makes it easier to add and remove items to the end of a list.
Examples
data Rgba = Rgba
{ red :: Int
, green :: Int
, blue :: Int
, alpha :: Int
}
1.12. Format export lists like arrays
Export lists should be formatted like regular arrays above (except with parentheses instead of square brackets.)
Examples
module Data.Array
( Array
, empty
, singleton
, sort
) where
1.13. Doubly indent nested record or array literals
When nesting record or array literals, the nested literal should be doubly indented (that is, indented 4 spaces.)
Examples
{ bounds:
{ x: 0
, y: 0
, width: 400
, height: 300
}
, color: Red
, children:
[ component1
, component2
, component3
]
}
1.14. Prefer case expressions over equational pattern matching
Case expressions should be used instead of equational pattern matching.
Rationale
If a function needs to be renamed when using a case expression in lieu of equational pattern maching, only a single edit needs to be made.
Exceptions
When matching multiple patterns, using equational pattern matching may be preferable.
Examples
catMaybes :: forall a. List (Maybe a) -> List a
catMaybes =
case _ of
Nil -> Nil
Nothing : xs -> catMaybes xs
Just x : xs -> x : catMaybes xs
2. Naming
2.1. Use camel case for function names
Functions must be written in camel case.
Examples
addfromMaybeisJust
Rationale
Syntactically, functions in PureScript must begin with a lower case letter.
2.2. Use upper camel case for type names
Data types and constructors must be written in upper camel case.
Examples
MaybeCommutativeRingSTRef
Rationale
Syntactically, data types and constructors in PureScript must begin with a capital letter.
2.3. Use all capitals for effect names
Effects should be written in all capital letters.
Examples
CONSOLEEXCEPTIONRANDOM
2.4. Do not use all capitals for acronyms
Acronyms should be capitalized like any other word.
Rationale
Names containing adjacent capitals that belong to separate words may hinder readability. Additionally, since function names must begin with a lower case letter, fully capitalizing all acronyms can be impossible.
Exceptions
Two letter acronyms should have both letters capitalized.
Examples
HtmlParserrgbToHslST
2.5. Use the singular for module names
Module names should be in the singular.
Examples
- Use
Data.Stringinstead ofData.Strings. - Use
Data.Functioninstead ofData.Functions.
3. Imports
3.1. Group imported modules by origin
Imports should be grouped in the following order:
- the
Prelude - other third-party imports
- local application or library imports
3.2. Sort imports alphabetically
The imports in each import group should be sorted alphabetically by module name.
3.3. Qualify imports or explicitly list imported symbols
Imported modules should either always be qualified or have explicit import lists.
Rationale
This makes your code more robust against changes in imported modules. It also makes your code compile without warnings.
Exceptions
The Prelude does not need to be qualified or have an implicit import list.
4. Comments
4.1. Use Markdown syntax in documentation comments
Markdown syntax should be used in documentation comments.
4.2. Comment every exported definition
Every exported function and data type should have a documentation comment.
5. Miscellaneous
5.1. Avoid over-using point-free style
Point-free style should be avoided when it inhibits readability.
5.2. Code must be warning-free
Code must not produce warnings when compiled.
Rationale
Ignoring warnings that are false positives or benign can eventually make it difficult to identify warnings that are serious.