The hlint package

[Tags: bsd3, library, program]

HLint gives suggestions on how to improve your source code.

[Skip to ReadMe]


Versions1.0.0.0,, 1.2, 1.4, 1.6, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.6.5, 1.6.6, 1.6.7, 1.6.8, 1.6.9, 1.6.10, 1.6.11, 1.6.12, 1.6.13, 1.6.14, 1.6.15, 1.6.16, 1.6.17, 1.6.18, 1.6.19, 1.6.20, 1.6.21, 1.7, 1.7.1, 1.7.2, 1.7.3, 1.8, 1.8.1, 1.8.2, 1.8.3, 1.8.4, 1.8.5, 1.8.6, 1.8.7, 1.8.8, 1.8.9, 1.8.10, 1.8.11, 1.8.12, 1.8.13, 1.8.14, 1.8.15, 1.8.16, 1.8.17, 1.8.18, 1.8.19, 1.8.20, 1.8.21, 1.8.22, 1.8.23, 1.8.24, 1.8.25, 1.8.26, 1.8.27, 1.8.28, 1.8.29, 1.8.30, 1.8.31, 1.8.32, 1.8.33, 1.8.34, 1.8.35, 1.8.36, 1.8.37, 1.8.39, 1.8.40, 1.8.41, 1.8.42, 1.8.43, 1.8.44, 1.8.45, 1.8.46, 1.8.47, 1.8.48, 1.8.49, 1.8.50, 1.8.51, 1.8.52, 1.8.53, 1.8.54, 1.8.55, 1.8.56, 1.8.57, 1.8.58, 1.8.59, 1.8.60, 1.8.61, 1.9, 1.9.1, 1.9.2, 1.9.3, 1.9.4, 1.9.5, 1.9.6, 1.9.7, 1.9.8, 1.9.9, 1.9.10, 1.9.11, 1.9.12, 1.9.13, 1.9.14, 1.9.15, 1.9.16, 1.9.17, 1.9.18, 1.9.19, 1.9.20, 1.9.21, 1.9.22, 1.9.23, 1.9.24, 1.9.25, 1.9.26, 1.9.27, 1.9.28, 1.9.29, 1.9.30, 1.9.31, 1.9.32
Change logCHANGES.txt
Dependenciesansi-terminal (>=0.6.2), base (==4.*), cmdargs (>=0.10), containers, cpphs (>=1.20.1), directory, extra (>=0.5), filepath, haskell-src-exts (==1.17.*), hlint, hscolour (>=1.21), process, refact (>=0.3), transformers, uniplate (>=1.5) [details]
CopyrightNeil Mitchell 2006-2016
AuthorNeil Mitchell <>
MaintainerNeil Mitchell <>
Home page
Bug tracker
Source repositoryhead: git clone
UploadedWed Mar 23 10:48:18 UTC 2016 by NeilMitchell
DistributionsArch:1.9.32, Debian:1.9.26, Fedora:1.9.10, FreeBSD:1.9.21, LTSHaskell:1.9.31, NixOS:1.9.32, Stackage:1.9.32
Downloads85743 total (255 in last 30 days)
2 []
StatusDocs uploaded by user
Build status unknown [no reports yet]




threadedBuild with support for multithreaded executionEnabledAutomatic
gplUse GPL libraries, specifically hscolourEnabledAutomatic

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info


Maintainers' corner

For package maintainers and hackage trustees

Readme for hlint-1.9.32

HLint Hackage version Stackage version Linux Build Status Windows Build Status

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. You can try HLint online at - suggestions are shown at the bottom. This document is structured as follows:


This program has only been made possible by the presence of the haskell-src-exts package, and many improvements have been made by Niklas Broberg in response to feature requests. Additionally, many people have provided help and patches, including Lennart Augustsson, Malcolm Wallace, Henk-Jan van Tuyl, Gwern Branwen, Alex Ott, Andy Stewart, Roman Leshchinskiy, Johannes Lippmann, Iustin Pop, Steve Purcell and others.

Bugs and limitations

Bugs can be reported on the bug tracker. There are some issues that I do not intend to fix:

Installing and running HLint

Installation follows the standard pattern of any Haskell library or program, type cabal update to update your local hackage database, then cabal install hlint to install HLint.

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

$ hlint darcs-2.1.2

darcs-2.1.2\src\CommandLine.lhs:94:1: Warning: Use concatMap
  concat $ map escapeC s
Why not:
  concatMap escapeC s

darcs-2.1.2\src\CommandLine.lhs:103:1: Suggestion: Use fewer brackets
  ftable ++ (map (\ (c, x) -> (toUpper c, urlEncode x)) ftable)
Why not:
  ftable ++ map (\ (c, x) -> (toUpper c, urlEncode x)) ftable

darcs-2.1.2\src\Darcs\Patch\Test.lhs:306:1: Warning: Use a more efficient monadic variant
  mapM (delete_line (fn2fp f) line) old
Why not:
  mapM_ (delete_line (fn2fp f) line) old

... lots more hints ...

Each hint says which file/line the hint relates to, how serious an issue it is, a description of the hint, 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 hint is marked as an warning, 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 suggestion include requiring an additional import, something not everyone agrees on, and functions only available in more recent versions of the base library.

Bug reports: The suggested replacement should be equivalent - please report all incorrect suggestions not mentioned as known limitations.

Automatically Applying Hints

By supplying the --refactor flag hlint can automatically apply most suggestions. Instead of a list of hints, hlint will instead output the refactored file on stdout. In order to do this, it is necessary to have the refactor executable on you path. refactor is provided by the apply-refact package, it uses the GHC API in order to transform source files given a list of refactorings to apply. Hlint directly calls the executable to apply the suggestions.

Additional configuration can be passed to refactor with the --refactor-options flag. Some useful flags include -i which replaces the original file and -s which asks for confirmation before performing a hint.

An alternative location for refactor can be specified with the --with-refactor flag.

Simple bindings for vim, emacs and atom are provided.

There are no plans to support the duplication nor the renaming hints.


HLint can generate a lot of information, making it difficult to search for particular types of errors. The --report flag will cause HLint to generate a report file in HTML, which can be viewed interactively. Reports are recommended when there are more than a handful of hints.

Language Extensions

HLint enables most Haskell extensions, disabling only those which steal too much syntax (currently Arrows, TransformListComp, XmlSyntax and RegularPatterns). Individual extensions can be enabled or disabled with, for instance, -XArrows, or -XNoMagicHash. The flag -XHaskell98 selects Haskell 98 compatibility.

Emacs Integration

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

(require 'hs-lint)
(defun my-haskell-mode-hook ()
   (local-set-key "\C-cl" 'hs-lint))
(add-hook 'haskell-mode-hook 'my-haskell-mode-hook)

GHCi Integration

GHCi integration has been provided by Gwern Branwen. The integration allows running :hlint from the GHCi prompt. The script is at hlint.ghci, and a copy is installed locally in the data directory. To use, add the contents to your GHCi startup 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.

If your version of GHC does not support the GHC threaded runtime then install with the command: cabal install --flags="-threaded"

C preprocessor support

HLint runs the cpphs C preprocessor over all input files, by default using the current directory as the include path with no defined macros. These settings can be modified using the flags --cpp-include and --cpp-define. To disable the C preprocessor use the flag -XNoCPP. There are a number of limitations to the C preprocessor support:

Unicode support

By default, HLint uses the current locale encoding. The encoding can be overridden with either --utf8 or --encoding=value. For descriptions of some valid encodings see the mkTextEncoding documentation.


Why are hints not applied recursively?


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:

Why doesn't the compiler automatically apply the optimisations?

HLint doesn't suggest optimisations, it suggests code improvements - the intention is to make the code simpler, rather than making the code perform faster. The GHC compiler automatically applies many of the rules suggested by HLint, so HLint suggestions will rarely improve performance.

Why doesn't HLint know the fixity for my custom !@%$ operator?

HLint knows the fixities for all the operators in the base library, but no others. HLint works on a single file at a time, and does not resolve imports, so cannot see fixity declarations from imported modules. You can tell HLint about fixities by putting them in a hint file, or passing them on the command line. For example, pass --with=infixr 5 !@%$, or put all the fixity declarations in a file and pass --hint=fixities.hs. You can also use --find to automatically produce a list of fixity declarations in a file.

How can I use --with or --hint with the default hints?

HLint does not use the default set of hints if custom hints are specified on the command line using --with or --hint. To include the default hints either pass --hint=HLint on the command line, or add import "hint" HLint.HLint in one of the hint files you specify with --hint.

Why do I sometimes get a "Note" with my hint?

Most hints are perfect substitutions, and these are displayed without any notes. However, some hints change the semantics of your program - typically in irrelevant ways - but HLint shows a warning note. HLint does not warn when assuming typeclass laws (such as == being symmetric). Some notes you may see include:

What is the difference between error/warning/suggestion?

Every hint has a severity level:

The difference between warning and suggestion is one of personal taste, typically my personal taste. If you already have a well developed sense of Haskell style, you should ignore the difference. If you are a beginner Haskell programmer you may wish to focus on warning hints before suggestion hints.

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:

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 "hint" HLint.Default
import "hint" HLint.Dollar

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 suppressing certain hints. Ignore directives can either be written as pragmas in the file being analysed, or in the hint files. Examples of pragmas are:

Ignore directives can also be written in the hint files:

These directives are applied in the order they are given, with later hints overriding earlier ones.

Adding hints

The hint suggesting concatMap is defined as:

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

This line can be read as replace concat (map f x) with concatMap f x. All single-letter variables are treated as substitution parameters. For examples of more complex hints see the supplied hints file. This hint will automatically match concat . map f and concat $ map f x, so there is no need to give eta-reduced variants of the hints. Hints may start with error, warn or suggest to denote how severe they are by default. In addition, hint is a synonym for suggest. If you come up with interesting hints, please submit them for inclusion.

You can search for possible hints to add from a source file with the --find flag, for example:

$ hlint --find=src/Utils.hs
-- hints found in src/Util.hs
warn = null (intersect a b) ==> disjoint a b
warn = dropWhile isSpace ==> trimStart
infixr 5 !:

These hints are suitable for inclusion in a custom hint file. You can also include Haskell fixity declarations in a hint file, and these will also be extracted. If you pass only --find flags then the hints will be written out, if you also pass files/folders to check, then the found hints will be automatically used when checking.