5

Haskell には、ブロックのインデント ベースのスタイルがあります。私は 2 つのスタイルを知っていますが、どちらのスタイルが優れているか判断できません。(本当にばかげた関数の例を許してください)

1位 - かわいい:

funcA :: Integer -> IO ()
funcA n = if n == 0
            then putStrLn "zero"
            else do putStr "--> "
                    print n

このスタイルは見栄えがしますが、非常に脆弱です。このコードをリファクタリングして名前を変更しましょfuncAう。の名前変更についても同じです。それは迷惑です。とても。thenelsedon

編集FUZxxl が述べたように、行はますます長くなりますが、ほとんどの行は先頭にスペースがあります。

別のスタイルはリファクタリングに適していますが、それほどきれいではありません。

funcA :: Integer -> IO ()
funcA n = if n == 0
    then putStrLn "zero"
    else do
        putStr "zero"
        print n

好きなスタイルとその理由は?おそらく、あなたは別のものを持っているか、説明付きの優れた開発者のコ​​ードスタイルへのリンクを持っていますか?

4

2 に答える 2

5

個人的には、両方のスタイルを組み合わせて使用​​しています。

次の「きれいな」インデント レイヤーが遠くない場合は、最初のスタイルを使用します

do foo
   bar
   baz

しかし、きれいなインデントによってコードが右側に偏りすぎてしまう場合は、空白を 2 つだけ使用します。

case foo of
  a -> bar
  b -> baz
  c -> quux

たとえば、20 個を超える空白のインデントがある場合、インデント レベルを「リセット」することがよくあります。

do a
   b <- do c -- normally I use only two whitespaces here
           d
           e <- do
     f
     g
     h

キーワードの直後にリストの最初のものを置くことができる場合、私はきれいなスタイルを使用する場合にのみそれを行います

 main = do x
           y
           z where
   a = b
   c = d
   e = f

あなたの例

funcA :: Integer -> IO ()
funcA n = if n == 0
  then putStrLn "zero"
  else do putStr "--> "
          print n

ただし、この例では、パターン マッチングと明示的なモナド演算 (この場合は>>) を使用するようにコードをリファクタリングし、do. が短い場合doは、しないでください。

funcA :: Integer -> IO ()
funcA 0 = putStrLn "zero"
funcA n = putStr "--> " >> print n
于 2012-06-25T19:20:21.343 に答える
1

私はまだ自分の「理想的な」Haskell スタイルを理解していません。私が現在コードを書く傾向にあるのは、おそらく Python と Mercury でのコーディングの影響を最も強く受けています。

私は、複数行の構造が明確な「ヘッダー」行を持つことを好む傾向があります。これは、詳細な読み取りなしで構造が何であるかを教えてくれます (これは一般的に、複数行構造の「形状」が最初または非常に決定される必要があることを意味します)。 「ヘッダー」行の終わり)、およびインデントを変更することによって明確に線引きされたいくつかの異なる複数行部分を持つ複数行構造の異なる「コンポーネント部分」を持つこと。したがって、あなたの例を次のように書く可能性がはるかに高くなります。

funcA :: Integer -> IO ()
funcA n =
    if n == 0 then
        putStrLn "zero"
    else
        do  putStr "--> "
            print n

またはおそらく:

funcA :: Integer -> IO ()
funcA n =
    if n == 0 then
        putStrLn "zero"
    else do
        putStr "--> "
        print n

と を同じ行elsedo折りたたみます (ただし、その場合は の後に新しい行を開始しますdo)。同様に、定義全体がdoブロックである関数は、通常、関数ヘッダーのdo直後に theがあり、インデントされた一連の行にブロックの実際のコードが続きます。=do

if/then/else の条件がより複雑な場合は、独自の「セクション」も指定して、次のようにします。

funcA :: Integer -> IO ()
funcA n =
    if
        n == 0
    then
        putStrLn "zero"
    else
        do  putStr "--> "
            print n

この if/then/else の形式は、GHC の比較的最近の変更に依存していると確信しています。以前は、thenelseの部分は よりも多くインデントする必要がありましたif。if/then/else ブロックの書き方が、そのルールの下で非常に快適であるとは思いませんでした。幸いなことに、Haskell ではパターン マッチングとガードのおかげであまり一般的ではありません。

このスタイルを使用する方法には、まだ完全に満足していない矛盾がいくつかあります。たとえば、現在開いているファイルには、次の形式の関数がいくつかあります。

foo a b = simple expression c d
    where
        c = bar a
        d = baz b

これは問題ないように見えますが、次のようになります。

foo a b =
    complex multiline expression c d
    where
        c = bar a
        d = baz b

論理的には、関数のwhere本体とは異なるインデント レベルにある必要があります。しかし、これは の一部ではなく の一部であるため、これ以上インデントするべきではありません。しかし、2 つのインデント レベルへのジャンプは見栄えが悪く、その後にブロックがあるかどうかによって正しいインデント レベルが決定されるのは不快なので、もう 1 レベルインデントして対応したくありません。幸いなことに、関数の定義がいくつかの補助的な定義の単純な式である場合に、私は主に使用しているようです。関数がいくつかの補助定義の大きな複雑な関数として頭に浮かんだ場合、私はそれをより独立した部分に分解しようとします。foo a b =complex ...complex ...wherewhere

このスタイルは、インデントの暴走をほとんど回避していると感じています (コードの他のビットの長さによってコードの正しいインデント位置を決定しないという点で「リファクタリングしやすい」とも言えます)。低レベル構造の詳細な解析を行う必要はありません。

Haskell の「開始位置よりもインデントする」というよりも、Pythonista (インデント レベルを使用) や Mercuryista(?) (セクションを含むコード構造を使用) のように考えすぎているのではないかと心配することがあります。アプローチ。しかし、物事が複雑になり始めるとすぐに、あなたの最初のきれいな例のようなものは、私の好みでは明らかに見栄えが悪く、読みにくくなります.

于 2012-06-27T02:26:03.137 に答える