4.8 KiB
We aim for a format of Haskell code that minimizes the amount of changes necessary when modifying a line. For instance the indentation of any given fragment of code should not depend on the length of identifiers.
longFunctionName :: a
-> b
-> c
-> d
-> (a, b, c, d)
Renaming the function requires changing all the lines to
f :: a
-> b
-> c
-> d
-> (a, b, c, d)
A preferred alternative is to format like
longFunctionName
:: a
-> b
-> c
-> d
-> (a, b, c, d)
We are grabbing some inspiration from Elm and Tweag's style guides.
The following examples show how a program is formatted in multiple lines when the user introduces linebreaks. Otherwise, the programs would be written in one line.
Function types
functionName
:: (C1, C2, C3)
=> a
-> b
-> c
-> d
-> (a, b, c, d)
Context
functionName
:: ( C1
, C2
, C3
, C4
, C5
)
=> a
-> b
-> c
-> d
-> (a, b, c, d)
Arguments
functionName
:: (C1, C2, C3, C4, C5)
=> a
-> b
-> (forall a.
(C6, C7)
=> LongDataTypeName
-> a
-> AnotherLongDataTypeName
-> b
-> c
)
-> (c -> d)
-> (a, b, c, d)
functionName
:: (C1, C2, C3, C4, C5)
=> a
-> b
-> ( LongDataTypeName
AnotherLongDataTypeName
AnotherLongDataTypeName2
AnotherLongDataTypeName3
-> a
-> AnotherLongDataTypeName4
-> b
-> c
)
-> (c -> d)
-> (a, b, c, d)
Expressions
f x y z =
if x then longFunctionName y else anotherLongFunctionName z
A lambda
f =
\(LongPattern x) ->
longFunctionName x
Multiple lambdas
f =
\(LongPattern x) ->
\(LongPattern2 y) ->
longFunctionName x
Lambda arguments
f xs =
forM xs $
\( LongPattern x
, LongPattern2 y
) ->
longFunctionName x
There is a do block. Note the change of indentation in the
body of forM
.
f xs = do
forM xs $
\( LongPattern x
, LongPattern2 y
) ->
longFunctionName x
if
expression
f x y z =
if x then
longFunctionName x y
else
anotherLongFunctionName x z
if
expression with monadic blocks
f x y z =
if x then do
longFunctionName1 x y
longFunctionName2 x y
else do
anotherLongFunctionName1 x z
anotherLongFunctionName2 x z
Monadic blocks
f x y z = do
functionName1 x y
functionName2 x z
case
expression
f x y z = case
Just w -> functionName x y w
Nothing -> functionName x y z
Branches of case
expression
f x y z = case
Just w ->
longFunctionName x y w
Nothing ->
anotherLongFunctionName x y z
A list of arguments
f
(LongPattern x)
(AnotherLongPattern y)
=
functionName x y
Guards
f x y
| x > y = True
| otherwise = False
The right-hand side of guards
f x y
| x > y =
longFunctionName y
| otherwise =
anotherLongFunctionName x
Long guards
function x y z w y
| x > y
, y > z
, z > w
, w > z
=
longFunctionName y
| otherwise =
anotherLongFunctionName x
Data type declarations
Declaration
data A
= B
| C
| D
| E
| F
| G
TODO: GADTs, record syntax, type families
Comments
{- When a comment is not preceded with elements on the same line,
they are indented as the elements in the enclosing AST node or
braces/parenthesis/brackets.
If it is between AST nodes, it is indented as the following
node.
If the end-of-file follows, then indent to column 0.
-}
f = g {- When there are preceding elements, keep single-line comments in the same line. -}
h = g
{- Multi-line comments can't be kept on the same line as @h = g@.
Otherwise, changing h or g would change the indentation of all
the comment lines.
-}
data R = R
{- This comment always follows the R constructor -}
{ f1 :: T1
{- A comment not preceded by other elements -}
{- Another comment -}
, f2 :: T2
, f3 :: T3
}
{- a comment line -}
f = h
where
h = g
-- indented the same as g
g = False
This example
data R = R {- This comment always follows the R constructor -}
{ f1 :: T1, {- An inner comment -} {- Another inner comment -} f2 :: T2, f3 :: T3}
is reformatted to
data R = R
{- This comment always follows the R constructor -}
{ f1 :: T1 {- An inner comment -} {- Another inner comment -}
, f2 :: T2
, f3 :: T3
}