Formatting + Style
in Haskell
Topics
- Motivation
- TVision Haskell Style Guide
-
Formatters
- Stylish, Brittany, Hindent, Ormolu
-
Lint
- HLint
-
Code Documentation
- Haddock
-
Style
- TVision Haskell Style Guide
-
Beyond
- File Watchers
Motivation
- Readability
- Writability
- Maintainability
The balance we choose impacts our code comprehension (hence correctness) and time spent onboarding/maintaining/iterating/reviewing/etc.
TVision Haskell Style Guide
Formatters
Stylish
- Formats data type declarations and imports
steps:
- simple_align:
cases: true
top_level_patterns: true
records: true
- imports:
align: none
list_align: after_alias
long_list_align: new_line_multiline
empty_list_align: inherit
list_padding: 2
separate_lists: true
- language_pragmas:
style: vertical
align: true
remove_redundant: true
- trailing_whitespace: {}
Stylish - Alignment
Datatype Alignment
- | is always aligned with =
-
, in fields is always aligned with {
- } is likewise always aligned with {
data User = User
{ name :: String
, homeAddress :: Address
, age :: Int
, cell :: PhoneNumber
}
data User = User
{ name :: String
, homeAddress :: Address
, age :: Int
, dob :: Date
}
Alignment can also be done on case statements and imports
Stylish - Imports
Import Sorting within manual groupings
import Control.Applicative ((<$>), liftA2, liftA3))
import System.Directory (doesFileExist)
import Data.Map (Map, keys, (!))
import qualified Data.Map as M
import System.Directory (doesFileExist)
import Control.Applicative (liftA3, (<$>), liftA2)
import qualified Data.Map as M
import Data.Map ((!), keys, Map)
Hindent
# hindent.yaml
tab-size: 2
line-length: 80
force-trailing-newline: true
Brittany
- https://github.com/lspitzner/brittany
- Comparison with hindent
- Designed to try and respect your intention w.r.t. blank lines, comments, parens, etc
- Configurable
- Seems to have the most docs out of all of the formatters
- Britanny Live
Brittany - Open Issues
-
Only the module header (imports/exports), type-signatures and function/value bindings are processed; other module elements (data-decls, classes, instances, etc.) are not transformed in any way;
- Extends to e.g. bindings inside class instance definitions - they won't be touched (yet).
-
There are some known issues regarding handling of in-source comments.
- There are cases where comments are not copied to the output (this will be detected and the user will get an error);
- There are other cases where comments are moved slightly; there are also cases where comments result in wonky newline insertion (although this should be a purely aesthetic issue.)
Ormolu
- Github - https://github.com/tweag/ormolu
- Ormolu Live - https://monadfix.com/ormolu
- "One True Style"
- Like "Black" for Haskell
- IMO writability++, readable--
HLint
HLint
-
A source linter for Haskell that provides a variety of hints on code improvements.
- Can be customised and configured with custom rules, on a per-project basis.
Custom Rules
-
Custom errors can be added in order to match and suggest custom changes of code from the left hand side match to the right hand side replacement
- error: {lhs: fmap, rhs: map}
- error: {lhs: mconcat, rhs: concat}
- error: {lhs: mapM, rhs: traverse}
- error:
{ lhs: getCurrentTime
, rhs: getCurrentTimeMicroseconds}
Disabling Rules
# Everywhere
- ignore: {name: Use let}
# In specific module
- ignore: {name: Use let, within: MyModule}
Using HLint
$ hlint some-haskell-package
some-haskell-package/src/Main.hs:13:3-44: Warning: Functor law
Found:
id <$> pure . someFunction
Perhaps:
pure . someFunction
some-haskell-package/src/Main.hs:34:42: Suggestion: Redundant $
Found:
$
Perhaps you should remove it.
Command line tool or editor configuration
apply-refact
hlint src/Main.hs --refactor \
--refactor-options="--inplace"
- https://hackage.haskell.org/package/apply-refact
- Applies refactorings specified by the refact package.
- Integrated into HLint to enable the automatic application of suggestions.
HLint 3.0
- Recently hit 3.0 (now 3.1.1)
- Moved from using haskell-src-exts to ghc-parser
Code Documentation
Haddock
- Personal page - https://tvision.atlassian.net/wiki/spaces/~242013112/pages/821723141/Haddock+Style+Guide+Braindump
- Stale HLint issue for linting Haddocks - https://github.com/ndmitchell/hlint/issues/456
- Ormolu expects well-formed Haddocks, and will do some stylizing for you!
Beyond
Automation
-
File Watchers
- Tools an watch files or trees of files and can be configured to execute whatever - linters, tools, tests, etc
- direvent, entr, fluffy, fswatch, gamin, incron, inoticoming, inotify-hookable, inotify-tools, systemd .path units, watchexec, watchman
-
ghcid
- "GHCi as a daemon", recompiles code on changes
- Git Hooks (particularly pre-commit)
- Editor Hooks and Plugins
Formatting + Style in Haskell
By Heneli Kailahi
Formatting + Style in Haskell
- 344