5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2023-11-28

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

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

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

参考「Haskell Style Guide :: Kowainik

1. 前提

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

悪い
f :: Int -> Int
f x =
    if x > 0
    then 23 * x
    else 0
良い
f :: Int -> Int
f x
    | x > 0     = 23 * x
    | otherwise = 0

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

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

参考「[Haskell] where 句は式でない - Qiita

悪い
f :: Int -> Int
f x =
    let y = 23 * x
    in  y
良い
f :: Int -> Int
f x = y
  where
    y = 23 * x

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

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

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

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

悪い
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 ()
良い
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 つずつでインデントします。

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` の前で改行する場合
  • 一部の =
    • 関数定義かつそれぞれの引数で改行したい場合
f :: Int -> Int
f x = y
  where
    y = 23 * x
import Control.Exception (catch, ErrorCall)
import System.IO (hPrint, stderr)

main :: IO ()
main = do

    do
        errorWithoutStackTrace "Error"
      `catch` \e -> do
        hPrint stderr (e :: ErrorCall)
f :: Int -> Int -> Int
f
    x
    y
  = x * y

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

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

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
    • 関数定義を含む変数束縛
悪い
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
良い
f :: Int -> Int
f x = y
  where
    y = 23 * x

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

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

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

class Bar a where
    bar :: a -> a

instance Bar Int where
    bar = (23 *)
悪い
f :: Int -> IO ()
f x = do putStr "Foo: "
         print x
良い
f :: Int -> IO ()
f x = do
    putStr "Foo: "
    print x

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

  • 一部の =
    • 関数定義かつそれぞれの引数で改行したい場合
悪い
f :: Int -> Int -> Int
f
    x
    y = x * y

g :: Int -> Int -> IO ()
g
    x
    y = do
        print $ x * y
良い
f :: Int -> Int -> Int
f
    x
    y
  = x * y

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

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

悪い
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"
良い
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 式の選択肢における -> の前後では改行しません。

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
5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?