HaskellStyleGuide
Style Guide for the Haskell code
IntroductionThese 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.
FilesUse 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. ImportsImports should be grouped into:
It is allowed to use qualified imports with short names for:
IndentationUse 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 stringsMultiline 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 declarationsWhen 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 spaceLike 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 commaInstead of ("a","b") write ("a", "b") NamingFunctions 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:
sum x y = x + y map (\v -> v + 1) lst 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 valuesSince 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 signaturesAlways 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 stylePrefer 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 MiscLanguage featuresExtensionsIt 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:
Such extensions should be declared using the Language pragma: {-# Language BangPatterns #-} {-| This is a small module… -} CommentsAlways 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 --. ToolsWe 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 |