[Haskell] インデントおよび改行のスタイルガイド

インデントおよび改行に関するスタイルガイドの例です。

主に以下のスタイルガイドを参考にしました。

(※完全な準拠はしていません。)

参考「[Haskell Style Guide :: Kowainik](https://kowainik.github.io/posts/2019-02-06-style-guide)」

# 1. 前提

なるべく `if-then-else` でなくガードを用います。

```haskell:悪い
f :: Int -> Int
f x =
    if x > 0
    then 23 * x
    else 0
```

```haskell:良い
f :: Int -> Int
f x
    | x > 0     = 23 * x
    | otherwise = 0
```

`do` 記法等を除いて、なるべく `let` 式でなく `where` 句を用います。

※ `where` 句は式でないため、関数定義を含む変数束縛で使用する必要があります。

参考「[[Haskell] where 句は式でない - Qiita](https://qiita.com/kerupani129/items/1600e6af3873015eabc7)」

```haskell:悪い
f :: Int -> Int
f x =
    let y = 23 * x
    in  y
```

```haskell:良い
f :: Int -> Int
f x = y
  where
    y = 23 * x

g :: Int -> IO ()
g x = do
    let y = 23 * x
    print y
```

`do` 記法の `let` は変数ごとに付けます。

```haskell:悪い
main :: IO ()
main = do
    let x = 23 :: Int
        y = 42 :: Int
    print $ x * y
```

```haskell:良い
main :: IO ()
main = do
    let x = 23 :: Int
    let y = 42 :: Int
    print $ x * y
```

`case` 式の選択肢における `->` の右側のコードは、関数定義を含む変数束縛を用いて分離し、小さくします。

```haskell:悪い
data T = Foo Int | Bar

f :: T -> Int
f x = case x of
    Foo y
        | y > 0     -> 23 * y
        | otherwise -> 0
    Bar -> 0

g :: T -> IO ()
g x = case x of
    Foo y -> do
        putStr "Foo: "
        print $ 23 * y
    Bar -> pure ()
```

```haskell:良い
data T = Foo Int | Bar

f' :: Int -> Int
f' x
    | x > 0     = 23 * x
    | otherwise = 0

f :: T -> Int
f x = case x of
    Foo y -> f' y
    Bar   -> 0

g' :: Int -> IO ()
g' x = do
    putStr "Foo: "
    print $ 23 * x

g :: T -> IO ()
g x = case x of
    Foo y -> g' y
    Bar   -> pure ()
```

# 2. インデント

基本は**スペース 4 つ**ずつでインデントします。

```haskell
data T = Foo | Bar

list :: [Int]
list =
    [ 10
    , 20
    , 30
    ]

f :: Int -> Int
f x
    | x > 0     = x * 42
    | otherwise = 0

main :: IO ()
main = do

    print $ f
        23

    print $ do
        Just 23 :: Maybe Int

    let x = Foo :: T
    putStrLn $ case x of
        Foo -> "Foo"
        Bar -> "Bar"

    -- 
    print list
```

ただし、以下の場合は**スペース 2 つ**にします:

- 一部の `where` 句
    - 関数定義を含む変数束縛
- `` `catch` `` の前で改行する場合
- 一部の `=`
    - 関数定義かつそれぞれの引数で改行したい場合

```haskell
f :: Int -> Int
f x = y
  where
    y = 23 * x
```

```haskell
import Control.Exception (catch, ErrorCall)
import System.IO (hPrint, stderr)

main :: IO ()
main = do

    do
        errorWithoutStackTrace "Error"
      `catch` \e -> do
        hPrint stderr (e :: ErrorCall)
```

```haskell
f :: Int -> Int -> Int
f
    x
    y
  = x * y

g :: Int -> Int -> IO ()
g
    x
    y
  = do
    print $ x * y
```

`do` 記法の `let` では変数名の位置を基準にインデントします。

```haskell
data T = Foo | Bar

main :: IO ()
main = do

    let list =
            [ 10
            , 20
            , 30
            ]
            :: [Int]

    let x = 23 :: Int
    let y
            | x > 0     = x * 42
            | otherwise = 0
    let z = z'
          where
            z' = y - 1729

    let w = do
            Just 23 :: Maybe Int

    let v = Foo :: T
    let u = case v of
            Foo -> "Foo"
            Bar -> "Bar"

    -- 
    print list
    print z
    print w
    print u
```

# 3. 改行

以下の場合は**必ず前後で**改行します:

- 一部の `where` 句
    - 関数定義を含む変数束縛

```haskell:悪い
f :: Int -> Int
f x = y where y = 23 * x

g :: Int -> Int
g x = y
  where y = 23 * x

g' :: Int -> IO ()
g' x = do
    print y
  where y = 23 * x

h :: Int -> Int
h x = y where
    y = 23 * x
```

```haskell:良い
f :: Int -> Int
f x = y
  where
    y = 23 * x

f' :: Int -> IO ()
f' x = do
    print y
  where
    y = 23 * x
```

以下の場合は**必ず後で**改行します:

- 一部の `where` 句
    - モジュール定義
    - クラス宣言
    - インスタンス宣言
- レイアウトを用いた `do` 記法

```haskell
module Foo where

class Bar a where
    bar :: a -> a

instance Bar Int where
    bar = (23 *)
```

```haskell:悪い
f :: Int -> IO ()
f x = do putStr "Foo: "
         print x
```

```haskell:良い
f :: Int -> IO ()
f x = do
    putStr "Foo: "
    print x
```

以下の場合は**必ず前で**改行します:

- 一部の `=`
    - 関数定義かつそれぞれの引数で改行したい場合

```haskell:悪い
f :: Int -> Int -> Int
f
    x
    y = x * y

g :: Int -> Int -> IO ()
g
    x
    y = do
        print $ x * y
```

```haskell:良い
f :: Int -> Int -> Int
f
    x
    y
  = x * y

g :: Int -> Int -> IO ()
g
    x
    y
  = do
    print $ x * y
```

演算子等があるところで改行したい場合、基本は演算子等の後でなく**前**で改行します。

```haskell:悪い
data T = Foo |
    Bar

list :: [Int]
list = [
    10,
    20,
    30
    ]

f :: Int -> Int -> Int
f = (*)

main :: IO ()
main = do

    print $
        f 23 42

    print $ f <$>
        Just 23 <*>
        Just 42

    -- 
    print list

    let x = Foo :: T
    putStrLn $ case x of
        Foo -> "Foo"
        Bar -> "Bar"
```

```haskell:良い
data T
    = Foo
    | Bar

list :: [Int]
list =
    [ 10
    , 20
    , 30
    ]

f :: Int -> Int -> Int
f = (*)

main :: IO ()
main = do

    print
        $ f 23 42

    print $ f
        <$> Just 23
        <*> Just 42

    -- 
    print list

    let x = Foo :: T
    putStrLn $ case x of
        Foo -> "Foo"
        Bar -> "Bar"
```

ただし、以下の場合は演算子の**後**で改行しても良いです:

- 一部の `=`
    - 関数定義を含む変数束縛
- 一部の `->`
    - 無名関数 

※関数定義を含む変数束縛の `= do` や無名関数の `-> do` では間で改行しません。
※ `case` 式の選択肢における `->` の前後では改行しません。

```haskell
import Data.Foldable (for_)

list :: [Int]
list =
    [ 10
    , 20
    , 30
    ]

f :: Int -> IO ()
f x = do
    print x

main :: IO ()
main = do

    print
        $ (\ (x :: Int) ->
            [ 10 * x
            , 20 * x
            , 30 * x
            ]
        )
        23


    for_ [1..3] $ \ (x :: Int) -> do
        print x

    -- 
    print list
    f 23
```