私はまだ自分の「理想的な」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
と を同じ行else
にdo
折りたたみます (ただし、その場合は の後に新しい行を開始します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 の比較的最近の変更に依存していると確信しています。以前は、then
とelse
の部分は よりも多くインデントする必要がありました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 ...
where
where
このスタイルは、インデントの暴走をほとんど回避していると感じています (コードの他のビットの長さによってコードの正しいインデント位置を決定しないという点で「リファクタリングしやすい」とも言えます)。低レベル構造の詳細な解析を行う必要はありません。
Haskell の「開始位置よりもインデントする」というよりも、Pythonista (インデント レベルを使用) や Mercuryista(?) (セクションを含むコード構造を使用) のように考えすぎているのではないかと心配することがあります。アプローチ。しかし、物事が複雑になり始めるとすぐに、あなたの最初のきれいな例のようなものは、私の好みでは明らかに見栄えが悪く、読みにくくなります.