My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
HaskellStyleGuide  
Style Guide for the Haskell code
Updated Jun 7, 2013 by mtart...@google.com

Introduction

These guidelines are intended to mirror the StyleGuide which is for the Python code.

The most important consideration is, as usual, to stay consistent with the existing code.

As there's no "canonical" style guide for Haskell, this code style has been inspired from a few online resources, among which http://snapframework.com/docs/style-guide, https://github.com/tibbe/haskell-style-guide/blob/master/haskell-style.md and http://www.cs.caltech.edu/courses/cs11/material/haskell/misc/haskell_style_guide.html.

Files

Use ordinary, non-literate Haskell (.hs files) (see http://www.haskell.org/haskellwiki/Literate_programming).

Use proper copyright headers, and proper Haddock style documentation headers:

{-| Short module summary.

Longer module description.

-}

{-

Copyright (C) …

This program is free software …

-}

If there are module-level pragmas add them right at the top, before the short summary.

Imports

Imports should be grouped into:

  • standard library imports
  • third-party imports
  • local imports

It is allowed to use qualified imports with short names for:

  • standard library (e.g. import qualified Data.Map as M)
  • local imports (e.g. import qualified Ganeti.Constants as C), although this form should be kept to a minimum

Indentation

Use only spaces, never tabs. Indentation level is 2 characters. For Emacs, this means setting the variable haskell-indent-offset to 2.

Line length should be at most 78 chars, and 72 chars inside comments.

Use indentation-based structure, and not braces/semicolons.

Special indendation of if/then/else construct: for the do notation, the if-then-else construct has a non-intuitive behaviour. As such, the indentation of if-then-else (both in do blocks and in normal blocks) should be as follows:

  …
  if condition
    then expr1
    else expr2

i.e. indent the then/else lines with another level. This can be accomplished in Emacs via the variable haskell-indent-thenelse to 2 (from the default of zero).

If you have more than one line of code pleas newline/indent after the "=":

Do not do that:

f x = let y = x + 1
      in  y

Instead do:

f x =
  let y = x + 1
  in  y

or if it is just one line:

f x = x + 1

Multiline strings

Multiline strings are created by closing a line with a backslash and starting the following line with a backslash, keeping the indentation level constant. Whitespaces go on the new line, right after the backslash.

longString :: String
longString = "This is a very very very long string that\
             \ needs to be split in two lines"

Data declarations

When declaring either data types, or using list literals, etc., the columns should be aligned, and for lists use a comma at the start of the line, not at the end. Note that this is different from the Python style! Example:

data OpCode = OpStartupInstance …
            | OpShutdownInstance …
            | …

data Node = Node { name :: String
                 , ip   :: String
                 , …
                 }

myList = [ value1
         , value2
         , value3
         ]

The choice of whether to wrap the first element or not is up to you, the following is also allowed:

myList = 
  [ value1
  , value2
  ]

White space

Like in Python, surround binary operators with one space on either side. Do no insert a space after a lamda:

-- bad
map (\ n -> …) lst
-- good
foldl (\x y -> …) …

Use a blank line between top-level definitions, but no blank lines between either the comment and the type signature or between the type signature and the actual function definition. Note: ideally it would be two blank lines between top-level definitions, but the code only has one now…

As always, no trailing spaces. Ever.

Spaces after comma

Instead of

("a","b")

write

("a", "b")

Naming

Functions should be named in mixedCase style, and types in CamelCase. Function arguments and local variables should be mixedCase.

When using acronyms, ones longer than 2 characters should be typed capitalised (e.g. Http), not fully upper-cased (HTTP).

For variable names, use descriptive names; it is only allowed to use very short names (e.g. a, b, i, j, etc.) when:

  • the function is trivial, e.g.:
  •     sum x y = x + y
  • we talk about some very specific cases, e.g. iterators or accumulators in folds:
  •     map (\v -> v + 1) lst
  • using x:xs for list elements and lists, etc.

In general, short/one-letter names are allowed when we deal with polymorphic values, for example the standard map definition from Prelude:

map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs

In this example, neither the a nor b types are known to the map function, so we cannot give them more explicit names. Since the body of the function is trivial, the variables used are longer.

However, if we deal with explicit types or values, their names should be descriptive (todo: add a nice example here).

Finally, the naming look familiar to people who just read the Prelude/standard libraries.

Naming for updated values

Since one cannot update a value in Haskell, it present a particular problem on the naming of new versions of the same value. For example, the following code in Python:

def failover(pri, sec, inst):
  pri.removePrimary(inst)
  pri.addSecondary(inst
  sec.removeSecondary(inst)
  sec.addPrimary(inst)

becomes in Haskell something like the following:

failover pri sec inst = 
  let pri'  = removePrimary pri inst
      pri'' = addSecondary pri' inst
      sec'  = removeSecondary sec inst
      sec'' = addPrimary sec' inst
  in (pri'', sec'')

When updating values, one should add single quotes to the name for up to three new names (e.g. inst, inst', inst'', inst'''), and otherwise use numeric suffixes (inst1, inst2, inst3, ..., inst8), but that many updates is already bad style and thus should be avoided.

Type signatures

Always declare types for functions (and any other top-level bindings). If in doubts, in a complex expression, feel free to declare the type of the variables/bindings; but this usually means the expression is too complex, so beware.

Similarly, provide Haddock-style comments for top-level definitions.

Parentheses, point free style

Prefer the so-called point-free style to extra parentheses:

-- bad
let a = f ( g ( h x) )
-- better
let b = f $ g $ h x
-- best
let c = f . g . h $ x

Misc

Language features

Extensions

It is recommended to keep the use of extensions to a minimum, so that the code can be understood even if one is familiar with just Haskel98/Haskell2010. That said, some extensions are very common and useful, so they are recommended:

  • BangPatterns: useful when you want to enforce strict evaluation (and better than repeated use of seq)
  • CPP: a few modules need this in order to account for configure-time options; don't overuse it, since it breaks multi-line strings
  • TemplateHaskell: we use this for automatically deriving JSON instances and other similar boiler-plate

Such extensions should be declared using the Language pragma:

{-# Language BangPatterns #-}

{-| This is a small module… -}

Comments

Always use proper sentences: start with a capital letter and use punctuation in top level comments:

-- | A function that does something.
f :: …

For inline comments, start with a capital letter but no ending punctuation. Furthermore, align the comments together with a 2-space width from the end of the item being commented:

data Maybe a = Nothing  -- ^ Represents empty container
             | Just a   -- ^ Represents a single value

The comments should be clear enough so that one doesn't need to look at the code to understand what the item does/is.

Use -- | to write doc strings rather than bare comment with --.

Tools

We generate the API documentation via Haddock, and as such the comments should be correct (syntax-wise) for it. Use markup, but sparingly.

We use hlint as a lint checker; the code is currently lint-clean, so you must not add any warnings/errors.

Use these two commands during development:

make hs-apidoc
make hlint

Sign in to add a comment
Powered by Google Project Hosting