HLint Manual

HLint is a tool for suggesting possible improvements to Haskell code. These suggestions include ideas such as using alternative functions, simplifying code and spotting redundancies. This document is structured as follows:

Installing and running HLint

FAQ

Customizing the hints

Acknowledgements

This program has only been made possible by the presence of the haskell-src-exts package, and many useful improvements have been made by Niklas Broberg in response to feature requests.

Bugs and limitations

A complete bug list is kept at my bug tracker.
Some common issues that I do not intend to fix include:

The presence of seq may cause some hints (i.e. eta-reduction) to change the semantics of the program.

The monomorphism restriction or uses of rank-2 types may sometimes cause transformed programs to become ill-typed.

Installing and running HLint

Once HLint is installed, simply run hlint source where source is either a Haskell file or a directory containing some Haskell files. Any directory will be searched recursively for any files ending with .hs or .lhs. For example, running HLint over darcs would give:

Each suggestion says which file/line the suggestion relates to, how serious the issue is, a description of the issue, what it found, and what you might want to replace it with. In the case of the first hint, it has suggested that instead of applying concat and map separately, it would be better to use the combination function concatMap.

The first suggestion is marked as an error, because using concatMap in preference to the two separate functions is always desirable. In contrast, the removal of brackets is probably a good idea, but not always. Reasons that a hint might be a warning include requiring an additional import, something not everyone agrees on, and functions only available in more recent versions of the base library.

Disclaimer: While these hints are meant to be correct, they aren't guaranteed to be. Please report any non equivalent hints not listed above in the limitations.

Reports

HLint can generate a lot of information, and often searching for either the errors specific to a file, or a specific class of errors, is difficult. Using the --report flag HLint will produce a report file in HTML, which can be viewed interactively. It is recommended that if investigating more than a handlful of hints, a report is used.

Emacs Integration

Emacs integration of HLint has been provided by Alex Ott. The integration is similar to compilation-mode, allowing navigation between errors, etc. The script is at hs-lint.el, and a copy is installed locally in the data directory. To use, just add the following code to the Emacs init file:

Parallel Operation

To run HLint on n processors append the flags +RTS -Nn, as described in the GHC user manual. HLint will usually perform fastest if n is equal to the number of physical processors.

FAQ

Why are suggestions not applied recursively?

Consider:

foo xs = concat (map op xs)

This will suggest eta reduction to concat . map op, and then after making that change and running HLint again, will suggest use of concatMap. Many people wonder why HLint doesn't directly suggest concatMap op. There are a number of reasons:

HLint aims to both improve code, and to teach the author better style. Doing modifications individually helps this process.

Sometimes the steps are reasonably complex, by automatically composing them the user may become confused.

Some people only make use of some of the suggestions. In the above example using concatMap is a good idea, but sometimes eta reduction isn't. By suggesting them separately, people can pick and choose.

Sometimes a transformed expression will be large, and a further hint will apply to some small part of the result, which appears confusing.

Consider f $ (a b). There are two valid hints, either remove the $ or remove the brackets, but only one can be applied.

Why aren't the suggestions automatically applied?

If you want to automatically apply suggestions, the Emacs integration offers such a feature. However, there are a number of reasons that HLint itself doesn't have an option to automatically apply suggestions:

The underlying Haskell parser library doesn't maintain sufficient position information to figure out exactly where the code came from.

The underlying parser doesn't preserve comments.

Sometimes multiple transformations may apply.

After applying one transformation, others that were otherwise suggested may become inappropriate.

If someone wanted to write such a feature, trying to work round some of the issues above, it would be happily accepted.

Customizing the hints

Many of the hints that are applied by HLint are contained in Haskell source files which are installed in the data directory by Cabal. These files may be edited, to add library specific knowledge, to include hints that may have been missed, or to ignore unwanted hints.

Choosing a package of hints

By default, HLint will use the HLint.hs file either from the current working directory, or from the data directory. Alternatively, hint files can be specified with the --hint flag. HLint comes with a number of hint packages:

Default - these are the hints that are used by default, covering most of the base libraries.

Dollar - suggests the replacement a $ b $ c with a . b $ c. This hint is especially popular on the #haskell IRC channel.

As an example, to check the file Example.hs with both the default hints and the dollar hint, I could type: hlint Example.hs --hint=Default --hint=Dollar. Alternatively, I could create the file HLint.hs in the working directory and give it the contents:

import HLint.Default
import HLint.Dollar

Adding hints

The hint suggesting concatMap is defined as:

error = concat (map f x) ==> concatMap f x

The line can be read as replace concat (map fx) with concatMap fx. Anything with a 1-letter variable is treated as a substitution parameter. For examples of more complex hints see the supplied hints file. In general, hints should not be given in point free style, as this reduces the power of the matching. Hints may start with error or warn to denote how severe they are by default.

If you come up with interesting hints, please submit them. For example, some of the hints about last were supplied by Henning Thielemann.

Ignoring hints

Some of the hints are subjective, and some users believe they should be ignored. Some hints are applicable usually, but occasionally don't always make sense. The ignoring mechanism provides features for supressing certain hints. Ignore directives are picked up from the hint files. Some example directives are: