You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Ryan Hendrickson edited this page Jun 20, 2021
·
1 revision
This is a style guide for the Haskell code that comprises the PureScript compiler. The recommended style guide for PureScript code is available here.
This page is a work in progress; compiler maintainers should currently feel free to debate and edit this document until it stops being contentious, at which point we may migrate it into the repo proper.
General Principles
In rough order from most to least important—that is, you can read each point as if it were followed by ‘when this doesn't conflict with a previous point’.
Code should be as readable as possible, assuming the reader is a proficient-but-not-legendary Haskell programmer.
Code should minimize the number of edits needed to make changes.
Code should be internally consistent—when practical, use the same approach to express similar ideas in nearby locations.
Less code is better.
Specific Cases
Whitespace
Line breaks
Try to format comments to wrap around 80 characters, and find ways to break up lines of code that are longer than 120-ish characters. If readability isn't served by this guideline, ignore it.
Indentation
Generally: two spaces, no tabs. When indentation is used to align elements of a syntactic form that spans multiple lines, use the indentation in a way that doesn't require every line to change if the initial line changes (principle 2).
... when declaring data types
Show code
Choose one:
dataEitherab=Lefta | Rightb
dataEitherab=Lefta
| Rightb
Do not write:
dataEitherab=Lefta
| Rightb
... when declaring type or kind signatures
Show code
Choose one:
foo::foralla.ClassNamea=>EitherTexta->a->a
foo
::foralla.ClassNamea=>EitherTexta->a->a
Do not write:
foo::foralla.ClassNamea=>EitherTexta->a->a
... when declaring values or functions
Show code
Choose one:
foo a b = bar a . baz $ qux b
foo a b
= bar a
. baz
$ qux b
Do not write:
foo a b = bar a
. baz
$ qux b
Intra-line alignment
Use sparingly?
Data types
Do use a custom data type instead of a Bool if at least one of the following applies:
the type is used in an exported member
a value of the type is used in a data structure which doesn't clarify the meaning of the value
it is easy to imagine a third option being added
Do declare a data type as a record if it has only one constructor and at least one of the following applies:
it is a simple newtype wrapper (prefix the accessor with one of the standard unwrapping words: get if the newtype is for instance selection (like using a particular Monoid), run if the newtype wraps a computation, un otherwise)
it has three or more arguments (prefix the accessor with an abbreviation of the type name)
it is expected to expand in the future (prefix the accessor with an abbreviation of the type name)
Do notation
Consider do notation if:
it reads best
it is used in adjacent branches
Avoid do notation if none of the above apply and:
no monadic work is being done
there is a simple way to express the computation directly with operators
Infix operators
Use infix notation for functions with the following names:
elem, notElem, member, notMember
cons, snoc
on
difference, intersect, union
isPrefixOf, isSuffixOf, generally anything named is*Of
Don't use infix notation for functions which would otherwise not merit it, just to avoid using flip: flip f x is equivalent to (`f` x), but the former is generally more readable. (???)
Pattern matching
Avoid catch-all patterns unless your logic is truly agnostic to new constructors being added to the type being matched.
Example
Do write:
--| Extract the value of a Foo.
maybeFooValue =\caseFoo value ->Just value
_ ->Nothing
Do not write:
--| Extract a wibble, which is something that may or may not be present in any ctor.
findWibble =\caseFoo wibble ->Just wibble
Bar _ wibble ->Just wibble
_ ->Nothing
When matching a data type, use the empty record pattern (CtorName{}) instead of wildcards (CtorName _ _) if the logic of the case would not change if new fields were added to that constructor.
Prefer lambda-case to multiple equational declarations, when possible. (This follows from principle 2; repeating the name of the function means more places to edit if that function is renamed.)