1. File Format
All Haskell source files start with a haddock header of the form:
A possible compiler pragma (like
may precede this header. The following hierarchical module name must of course match the file name.
Make sure that the description is changed to meet the module (if the header was copied from elsewhere). Insert your email address as maintainer.
Try to write portable (Haskell98) code. If you use e.g. multi-parameter type classes and functional dependencies the code becomes “non-portable (MPTC with FD)”.
The $Header$ entry will be automatically expanded.
Don’t leave trailing white space in your code in every line.
Expand all your tabs to spaces to avoid the danger of wrongly expanding them (or a different display of tabs versus eight spaces). Possibly put something like the following in your ~/.emacs file.
(custom-set-variables ‘(indent-tabs-mode nil))
The last character in your file should be a newline. Under solaris you’ll get a warning if this is not the case and sometimes last lines without newlines are ignored.
You may use \url{http://hackage.haskell.org/package/scan} to check your file format.
The whole module should not be too long (about 400 lines).
2. Required Elements
instead of just:
And, good type signatures lead to better error messages.
Respect compiler warnings.
3. Formatting
Indent your code blocks with 4 spaces. Indent the where keyword two spaces to set it apart from the rest of the code and indent the definitions in a where clause 2 spaces. Some examples:
No blank lines between type signatures and function definitions.
Add one blank line between functions in a type class instance declaration if the functions bodies are large. Use your judgement. Whitespace
Surround binary operators with a single space on either side. Use your better judgement for the insertion of spaces around arithmetic operators but always be consistent about whitespace on either side of a binary operator. Don’t insert a space after a lambda. Data Declarations
Align the constructors in a data type definition. Example:
For long type names the following formatting is also acceptable:
Format records as follows:
Align the -> arrows when it helps readability.
standard library imports
related third party imports
local application/library specific imports
Put a blank line between each group of imports. The imports in each group should be sorted alphabetically, by module name.
Always use explicit import lists or qualified imports for standard and third party libraries. This makes the code more robust against changes in these libraries. Exception: The Prelude.
4. Naming
In Haskell types start with capital and functions with lowercase letters, so only avoid infix identifiers. Defining symbolic infix identifiers should be left to library writers only.
Argument naming and documentation is important sometimes.
For a function like replicate :: Int -> a -> [a], it’s pretty obvious what each of the arguments does, from their types alone. For a function that takes several arguments of the same type, like isPrefixOf :: (Eq a) => [a] -> [a] -> Bool, naming/documentation of arguments is more important.
5. Comments
Separate end-of-line comments from the code using 2 spaces. Align comments for data type definitions.
It should be obvious how the code works, just by looking at it. If this is not the case, you need to rewrite the code. Do not over-comment. Comments should be written using correct spelling and grammar in complete sentences with punctation (in English only).
Very many or very long comments (especially within the body of a function) are more distracting than helpful. Long comments may appear at the top of a file or section of code if you need to explain the overall design of the code or refer to any sources that have more information about the algorithms or data structures. All other comments in the file should be as short as possible. Judicious choice of variable names can help minimize the need for comments.
“Generally, you want your comments to tell WHAT your code does, not HOW. Also, try to avoid putting comments inside a function body: if the function is so complex that you need to separately comment parts of it, you should probably decompose it.
Avoid comments that state the obvious.
6. Functionality
Most haskell functions should be at most a few lines, only case expression over large data types (that should be avoided, too) may need corresponding space.
The code should be succinct (though not obfuscated), readable and easy to maintain (after unforeseeable changes). Don’t exploit exotic language features without good reason.
It’s not fixed how deep you indent (4 or 8 chars). You can break the line after “do”, “let”, “where”, and “case .. of”. Make sure that renamings don’t destroy your layout. (If you get to far to the right, the code is unreadable anyway and needs to be decomposed.)
Good:
Avoid the notation with braces and semicolons since the layout rule forces you to properly align your alternatives.
Prefer a space between a function and its argument, even if the argument is parenthesized.
Usually a case statement (and the import of isJust and fromJust from Data.Maybe) can be avoided by using the “maybe” function:
maybe (error “
Be more explicit about failure cases. Surely a missing (or an irrefutable) pattern would precisely report the position of a runtime error, but these are not so obvious when reading the code. Missing unreachable cases can be filled in using “error” with a fixed string “
7. Let or where expressions
Avoid mixing and nesting “let” and “where”. Take advantage of where clauses, especially their ability to see function parameters that are already in scope (nice vague advice). If you are really grokking Haskell, your code should have a lot more where-bindings than let-bindings. Too many let-bindings is a sign of an unreconstructed ML programmer or Lisp programmer.
If one function exists only to serve another function, and isn’t otherwise useful, and/or it’s hard to think of a good name for it, then it probably should exist in it’s caller’s where clause instead of in the module’s scope.
Use auxiliary top-level functions that you do not export. Export lists also support the detection of unused functions.
8. Pattern Matching
Preferred version:
vs.
9. Verbosity
10. Types
Prefer proper data types over type synonyms or tuples even if you have to do more constructing and unpacking. This will make it easier to supply class instances later on. Don’t put class constraints on a data type, constraints belong only to the functions that manipulate the data.
Using type synonyms consistently is difficult over a longer time, because this is not checked by the compiler. (The types shown by the compiler may be unpredictable: i.e. FilePath, String or [Char])
Take care if your data type has many variants (unless it is an enumeration type.) Don’t repeat common parts in every variant since this will cause code duplication.
11. Application Notation
Many parentheses can be eliminated using the infix application operator “$” with lowest priority. Try at least to avoid unnecessary parentheses in standard infix expression.
12. List Comprehensions
Use these only when “short and sweet”. Prefer map, filter, and foldr.
13. IO
Try to strictly separate IO, Monad and pure (without do) function programming (possibly via separate modules).
Don’t use Prelude.interact and make sure your program does not depend on the (not always obvious) order of evaluation. E.g. don’t read and write to the same file:
14. Dealing with laziness
By default, use strict data types and lazy functions.
14.1. Data types
Constructor fields should be strict, unless there’s an explicit reason to make them lazy. This avoids many common pitfalls caused by too much laziness and reduces the number of brain cycles the programmer has to spend thinking about evaluation order.
Additionally, unpacking simple fields often improves performance and reduces memory usage:
As an alternative to the UNPACK pragma, you can put
at the top of the file. Including this flag in the file inself instead of e.g. in the Cabal file is preferable as the optimization will be applied even if someone compiles the file using other means (i.e. the optimization is attached to the source code it belongs to).
Note that -funbox-strict-fields applies to all strict fields, not just small fields (e.g. Double or Int). If you’re using GHC 7.4 or later you can use NOUNPACK to selectively opt-out for the unpacking enabled by -funbox-strict-fields.
14.2. Functions
Have function arguments be lazy unless you explicitly need them to be strict.
The most common case when you need strict function arguments is in recursion with an accumulator:
15. DRY
Use Template-Haskell when appropriate. Bundles of functions like zip3, zipWith3, zip4, zipWith4, etc are very meh. Use Applicative style with ZipLists instead. You probably never really need functions like those.
Derive instances automatically. The derive package can help you derive instances for type-classes such as Functor (there is only one correct way to make a type an instance of Functor).
16. Generalize the Code
Code that is more general has several benefits:
It’s more useful and reusable.
It is less prone to bugs because there are more constraints. For example if you want to program concat :: [[a]] -> [a], and notice how it can be more general as join :: Monad m => m (m a) -> m a. There is less room for error when programming join because when programming concat you can reverse the lists by mistake and in join there are very few things you can do.
17. Monad Transformers
When using the same stack of monad transformers in many places in your code, make a type synonym for it. This will make the types shorter, more concise, and easier to modify in bulk.
If your type is logically an instance of a type-class, make it an instance.
The instance can replace other interface functions you may have considered with familiar ones. Note: If there is more than one logical instance, create newtype-wrappers for the instances. Make the different instances consistent. It would have been very confusing/bad if the list Applicative behaved like ZipList.
References:
http://www.seas.upenn.edu/~cis552/12fa/styleguide.html
http://www.haskell.org/haskellwiki/Programming_guidelines
https://github.com/tibbe/haskell-style-guide/blob/master/haskell-style.md
https://stackoverflow.com/questions/1983047/good-haskell-coding-standards