Haskell の優れたコーディング標準へのリンクを提供してくれる人はいますか? thisとthisを見つけましたが、包括的ではありません。言うまでもなく、HaskellWiki には「慎重にクラスを使用する」や「シンボリック中置識別子の定義はライブラリ作成者のみに任せるべき」などの「宝石」が含まれています。
5 に答える
本当に難しい質問です。あなたの答えが何か良いものになることを願っています。一方、初心者向けのコードで見つけた間違いやその他の厄介なもののカタログを次に示します。Kornel Kisielewicz が指摘する Cal Tech のスタイル ページと重複する部分があります。私のアドバイスのいくつかは、HaskellWiki の「宝石」と同じくらい曖昧で役に立たないものですが、少なくともより良いアドバイスになることを願っています :-)
80 列に収まるようにコードをフォーマットします。(上級ユーザーは 87 または 88 を好むかもしれません。それ以上はそれを推し進めています。)
let
バインディングと句は定義のシーケンスではなくwhere
、相互に再帰的な定義のネストを作成することを忘れないでください。where
句、特に既にスコープ内にある関数パラメータを表示する機能を活用してください (あいまいなアドバイスですが)。Haskell を本当によく理解しているのであれば、コードには -bindingswhere
よりも多くの -bindings が必要let
です。バインディングが多すぎるのlet
は、再構築されていない ML プログラマーまたは Lisp プログラマーの兆候です。冗長な括弧は避けてください。冗長な括弧が特に不快な場所は次のとおりです。
式の条件の周り
if
(再構成されていない C プログラマーとしてブランド化します)それ自体が中置演算子の引数である関数適用の周り (関数適用は、どの中置演算子よりも強く結合します。この事実は、私たち恐竜が APL の右から左への走査規則を持っていたのと同じように、すべての Haskeller の脳に焼き付けられるべきです。燃えた。)
中置演算子の前後にスペースを入れてください。タプル リテラルでは、各コンマの後にスペースを入れます。
引数が括弧で囲まれている場合でも、関数とその引数の間にスペースを入れてください。
演算子を慎重に使用
$
して、括弧を減らしてください。$
と infixの密接な関係に注意してください.
。f $ g $ h x == (f . g . h) x == f . g . h $ x
ビルトイン
Maybe
とEither
型を見逃さないでください。絶対に書かないでください
if <expression> then True else False
。正しいフレーズは単純に<expression>
です。パターン マッチングを使用できる場合は
head
、 orを使用しないでください。tail
中置ドット演算子による関数合成を見逃さないでください。
改行は慎重に使用してください。改行は読みやすさを向上させますが、トレードオフがあります: エディターは一度に 40 ~ 50 行しか表示しない場合があります。大きな関数を一度に読んで理解する必要がある場合は、改行を使いすぎてはいけません。
ほとんどの場合、コメントより
--
も行末まで続くコメントが好まれ{- ... -}
ます。中括弧で囲まれたコメントは、大きなヘッダーに適している場合があります。それだけです。各トップレベル関数に明示的な型シグネチャを与えます。
可能であれば、
--
行、=
記号、さらには隣接する行にある括弧やコンマを揃えます。私は GHC セントラルの影響を受けており、
camelCase
エクスポートされた識別子に使用することとshort_name
、ローカルにwhere
バインドされた変数またはlet
バインドされた変数にアンダースコアを使用することを非常に控えめに好みます。
私見の良い経験則:
- HLintに相談して、冗長な中かっこを使用していないこと、およびコードが無意味にポイントフルになっていないことを確認してください。
- 既存のライブラリ関数を再作成することは避けてください。Hoogleはそれらを見つけるお手伝いをします。
- 多くの場合、既存のライブラリ関数は、作成しようとしていたものよりも一般的です。たとえば、 が必要な場合
Maybe (Maybe a) -> Maybe a
はjoin
、とりわけそれを行います。
- 多くの場合、既存のライブラリ関数は、作成しようとしていたものよりも一般的です。たとえば、 が必要な場合
- 引数の命名とドキュメントは重要な場合があります。
- のような関数の場合
replicate :: Int -> a -> [a]
、各引数が何をするかは、その型だけから明らかです。 - のように、同じ型の複数の引数を取る関数の場合、引数の
isPrefixOf :: (Eq a) => [a] -> [a] -> Bool
名前付け/ドキュメント化がより重要になります。
- のような関数の場合
- ある関数が別の関数を提供するためだけに存在し、それ以外の場合は役に立たない場合、および/または適切な名前を考えるのが難しい場合
where
は、モジュールのスコープではなく、呼び出し元の句に存在する必要があります。 - ドライ
- 適切な場合は Template-Haskell を使用してください。
zip3
、zipWith3
、zip4
、などの関数のバンドルzipWith4
は非常に厄介です。代わりに s でApplicative
style を使用してください。ZipList
おそらく、そのような機能が本当に必要になることはありません。- インスタンスを自動的に派生させます。派生パッケージは、次のような型クラスのインスタンスを派生させるのに役立ちます
Functor
(型を のインスタンスにする正しい方法は 1 つだけですFunctor
)。
- より一般的なコードには、いくつかの利点があります。
- より便利で再利用可能です。
- より多くの制約があるため、バグが発生しにくくなります。
- たとえば、 をプログラムしたい場合
concat :: [[a]] -> [a]
、 としてより一般的になることに注意してjoin :: Monad m => m (m a) -> m a
ください。プログラミング時に誤ってリストを逆にする可能性があり、できることはほとんどないjoin
ため、プログラミング時にエラーが発生する余地が少なくなります。concat
join
- たとえば、 をプログラムしたい場合
- コードの多くの場所でモナド変換子の同じスタックを使用する場合は、その型シノニムを作成します。これにより、型がより短く、より簡潔になり、一括で変更しやすくなります。
- 「怠惰な IO」に注意してください。たとえば
readFile
、ファイルが読み取られる時点でファイルの内容を実際に読み取るわけではありません。 - コードが見つからないほどインデントしないでください。
- 型が論理的に型クラスのインスタンスである場合は、それをインスタンスにします。
- このインスタンスは、あなたが考えたことのある他のインターフェース機能を使い慣れたものに置き換えることができます。
- 注: 複数の論理インスタンスがある場合は、インスタンスの newtype-wrappers を作成します。
- 異なるインスタンスを一貫させます。
Applicative
リストが のように振る舞うと、非常に混乱/悪いことになりますZipList
。
私は、次のようなことを行うことで、関数を可能な限りポイントフリー スタイルの構成として整理しようとするのが好きです。
func = boo . boppity . bippity . snd where boo = ... boppity = ... bippity = ...
ネストされた括弧や長い括弧で囲まれた式を避けるためだけに ($) を使用するのが好きです
... 私は自分の中にもう少しあると思っていました。
このスタイルチェッカーをご覧になることをお勧めします。
Haskell コード スタイルのほぼすべての側面をカバーする優れたマークダウン ファイルを見つけました。チートシートとして使用できます。ここで見つけることができます:リンク