haskell-style.md 8.84 KB
Newer Older
1
2
3
4
# Haskell Style Guide

This is [Khumba's](http://khumba.net) style guide for Haskell source code.

5
*Copyright 2015, 2016 Bryan Gardiner.  Licensed under the
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/).*

## Formatting

This section contains style guidelines for the visual layout of code.

### Encoding

Haskell source files are UTF-8, so use printable non-ASCII characters freely in
strings (use escaped syntax for non-printable characters).  Don't use the
Unicode equivalents of `->`, `=>`, `::`, `forall`, etc.

### Line length

20
21
22
Maximum 100 characters, but feel free to wrap earlier than necessary (e.g. it's
easier to read 80-character English paragraphs than 100-character).  Exceptions
are things that shouldn't be broken, for example long paths or URLs.
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

### Whitespace

Put one blank line after the `module` declaration, around the entire import
group, and between top-level declarations.  Also use a blank lines between
definitions if at least one of the definitions takes more than a single line
(e.g. because of including a type declaration or comments) in `let`, `where`,
`class` and `instance`, etc.  (Optional for record fields.)

Except at the start or end of a line, put one space around binary operators; do
not align operators in the middle of lines:

    let abacus = 3
        cake   = 1  -- No.

For end-of-line comments, put two spaces before the start of the comment.

Remove trailing whitespace.

### Breaking expressions across multiple lines

Breaking large statements across multiple lines can be necessary to meet the
100-character limit and also can improve readability.  Prefer to break at outer
expressions rather than inner ones, for example for the following line,

    someVariable = someAction . foo =<< someOtherAction
                  A            C       B

breaking at `A` would be best, and `B` would be second-best, and `C` would be
bad and misleading, although the following would be okay because there is a
natural flow from right to left:

    someVariable =
      someAction .
      foo =<<
      someOtherAction

Should you break a line before or after an operator?  That depends on the
operator.  Unless otherwise specified, operators should go at the end of the
line.  This includes `::` and `=`, which makes it convenient to search for
e.g. `"someBinding ::"`.  Operators which when wrapped should go at the start of
a line are:

- The function type `->` and class constraint specifier `=>`.
- List and record value separator `,` and closing characters `]` and `}`.
- `)`, only in a module export list.
- `|` between data constructors in a data declaration.

Function types can be wrapped like

    divideM ::
      (Num a, Show a, Monad m) => a -> a -> m a

or

    divideM :: (Num a, Show a, Monad m)
            => a  -- ^ Divisor.
            -> a  -- ^ Quotient.
            -> m a

or, if single types are really long or if you want to put any parameter
documentation onto its own line(s), then use:

    divideM ::
         (Num a, Show a, Monad m)
      => a  -- ^ This is the divisor.
      -> a
         -- ^ This is the quotient.  Multi-line comments get to start on their
         -- own line.
      -> m a

When wrapping function calls, indent subsequent lines to the level of the first
function argument:

    someFunction foo bar baz
                 quux
                 quuux

or:

    someFunction
      foo bar baz
      quux
      quuux

### Indentation

Use tabs, not spaces.  When line breaking to start an indented expression after
a function arrow `->`, `case ... of`, `do`, `let ... in`, `where`, etc.,
increase the indentation by two spaces.  When writing an `if` or `let` within a
`do`, indent the `true`, `false`, and `in` parts two spaces.

### The application operator

`$` versus parentheses: prefer the shorter one for your use case, choosing `$`
118
119
120
if the lengths are equal (which they will be in the common case).  Generally `$`
is preferred, but parentheses may be better in other cases, for example when
there is symmetry between a function's arguments:
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

    foo (bar a) (bar b)

versus:

    foo (bar a) $ bar b

Instead of writing:

    foo . bar . baz $ quux x

prefer this:

    foo $ bar $ baz $ quux x

### Lists

Single-line lists should be formatted like `[foo, bar, baz]` without spaces on
139
the inside of the brackets.  Multi-line lists can use either the compact
140
141
142
143

    [foo, bar,
     baz, quux, quuux]

144
or one-line-per-element
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

    [ foo
    , bar
    , baz
    , quux
    , quuux
    ]

formats.  The former is preferred for short lists of fixed length since it
doesn't require an extra line for the closing bracket, and the latter is
preferred for lists that may grow gradually since it makes diffs look nicer.

### Tuples

If a tuple `(foo, bar, baz)` doesn't fit on a single line, it should be wrapped
in the first multi-line list style above, since tuples also have fixed length.

### Data types

Data types with a single data constructor using record syntax are formatted
like:

    data Foo = Foo
      { fooA :: A
      , fooB :: B
      } deriving (Eq, Ord, Show)

Data types with multiple constructors and without field names are formatted
like:

    data Choice =
        Yes
      | No
      | FileNotFound
        -- ^ The obvious choice.

## Language use

This section contains style guidelines for the use of Haskell language features.

### Module imports and exports

[Import modules properly:](https://wiki.haskell.org/Import_modules_properly) do
not import the entire contents of another package's module unqualified.

Sort all imports alphabetically by module name.  When a module is imported in
both qualified and unqualified form, put the qualified form first.

Single- or few-letter qualified module names (`import qualified
Data.ByteString.Lazy as BL`) are fine as long as you are consistent with your
abbreviation usage in your project.

197
198
Every module with a `module` line should have an export list.  A complete module
or import declaration should either fit on one line:
199
200
201

    module My.Awesome.Module (foo, bar, baz, quux, quuux) where

202
203
    import Other.Awesome.Module (asdf, zxcv, qwerty)

204
205
206
207
208
209
210
211
212
213
214
or it should be wrapped as follows:

    module My.Awesome.Module (
      foo, bar, baz (..),
      quux (a, b, c),
      quuux (
        m, n, o,
        p, q, r
      ),
      ) where

215
216
217
218
219
220
221
222
    import Other.Awesome.Module (
      asdf, zxcv, qwerty,
      om (nom, onom),
    )

with commas at the end of lines that allow them.  Outer imports/exports can
always be followed by a comma, but a final trailing comma is not allowed in a
sub-import/export.
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290

### Type signatures

All top-level bindings should include type signatures.  Type signatures are not
required on `let` or `where` bindings.

When writing a type with class constraints (`=>`), or a function type (`->`),
the type should either fit on a single line, or else have each `=> ...` or
`-> ...` section on its line.

### Data types

Avoid using record syntax for data types with multiple data constructors, since
the record accessors are partial functions.  Factor the records out into
separate data types, or if all the records' fields are the same, use one data
constructor with an enum inside.

### Typeclasses

Typeclasses are amazing, but don't write classes that are not expected to take
advantage of polymorphism from multiple instances.  Do not write orphan
instances.

### Haddock comments

Modules, and all bindings exported from modules, should have Haddock
documentation attached to them.  Use `-- |` on the line before the declaration,
except when including the comment on the same line as the declaration, in which
case `-- ^` is needed, or when documenting function parameter types or record
fields.  Don't use `{- | -}` or `{- ^ -}`.

    -- | This function does nothing.
    nop :: IO ()
    nop x =
      let -- | Actually unused!
          y = f x
      in return ()

    -- | This function does something.
    doSomething ::
         String
         -- ^ Separate-line comment.
      -> String
         -- ^ Another separate-line comment.
      -> Int  -- ^ Inline comment.
      -> ()

    data MyRecord = MyRecord
      { earthAgeMillis :: Long
        -- ^ Age of the earth in milliseconds.
      , favouriteColour :: Colour
        -- ^ My colour of choice.
      , dirtyBool :: Bool  -- ^ Depends on the weather.
      }

The `--` for a Haddock comment should be aligned with the identifier to which it
belongs.

Haddock comments should use complete sentences, although the first sentence of a
comment may be a sentence fragment referring to the thing being documented.

Use hyperlinks liberally.  If you're going to `@wrap@` a name, you might as well
`'link'` it.

### Redundancy, lint, warnings

Avoid reduncancy, refactoring when appropriate.  This also applies to syntactic
elements: clean up unused imports, remove unnecessary `do`s, and in general
291
292
listen to the suggestings of HLint.  Code should compile warning-free with `-W
-fwarn-incomplete-patterns -fwarn-unused-do-bind`.