web-dev-qa-db-ja.com

優れたHaskellコーディング標準

誰かがHaskellの優れたコーディング標準へのリンクを提供できますか? thisthis は見つかりましたが、包括的ではありません。言うまでもなく、HaskellWikiには、「注意してクラスを使用する」、「シンボリックインフィックス識別子の定義はライブラリの作成者のみに任せる」などの「gem」が含まれています。

74
Alexey Romanov

本当に難しい質問です。あなたの答えが何か良いものになることを願っています。一方、これは私が初心者のコードで見つけた間違いやその他の迷惑なもののカタログです。 Kornel Kisielewiczが指摘しているCal Techスタイルのページと一部重複しています。私のアドバイスのいくつかは、HaskellWikiの「宝石」のように曖昧で役に立たないものですが、少なくともそれがより良いアドバイスであることを願っています:-)

  • 80列に収まるようにコードをフォーマットします。 (上級ユーザーは87または88を好むかもしれませんが、それを超えるのはプッシュです。)

  • letバインディングとwhere句は相互に再帰的な定義のネストを作成することを忘れないでくださいnot a sequence定義。

  • where句を活用します。特に、すでにスコープ内にある関数パラメーターを表示する機能(ナイスな漠然としたアドバイス)を活用してください。 Haskellを実際に操作している場合、コードにはwhere- bindingsよりも多くのlet- bindingsが必要です。 let- bindingsが多すぎることは、再構築されていないMLプログラマーまたはLISPプログラマーの兆候です。

  • 括弧の重複を避けてください。余分な括弧が特に攻撃的であるいくつかの場所は

    • if式の条件の周り(再構築されていないCプログラマーとしてあなたをブランド化する)

    • それ自体が中置演算子の引数である関数アプリケーションの周り(関数アプリケーションは、あらゆる中置演算子よりも強くバインドされます。この事実は、私たちの恐竜がAPLを持っていたのとほぼ同じ方法で、すべてのHaskellerの脳に焼き付けられるはずです右から左へのスキャンルールが組み込まれています。

  • 中置演算子の周りにスペースを入れます。タプルリテラルの各コンマの後にスペースを入れます。

  • 引数が括弧で囲まれている場合でも、関数とその引数の間にはスペースを入れてください。

  • $演算子を慎重に使用して、かっこを減らします。 $とインフィックス.の密接な関係に注意してください:

    f $ g $ h x == (f . g . h) x == f . g . h $ x
    
  • 組み込みのMaybeおよびEither型を見落とさないでください。

  • 決して書きませんif <expression> then True else False;正しいフレーズは単に<expression>です。

  • パターンマッチングを使用できる場合は、headまたはtailを使用しないでください。

  • Infixドット演算子で関数の合成を見落とさないでください。

  • 改行は慎重に使用してください。改行すると読みやすくなりますが、トレードオフがあります。エディターは一度に40〜50行しか表示しない場合があります。大きな関数を一度に読んで理解する必要がある場合は、改行を多用しないでください。

  • ほとんどの場合、--コメントよりも行末まで実行される{- ... -}コメントを優先します。中かっこで囲まれたコメントは、大きなヘッダーに適している場合があります。

  • 各トップレベル関数に明示的な型シグニチャーを与えます。

  • 可能な場合は、--行、=記号、さらには隣接する行にある括弧とコンマを揃えます。

  • 私はGHCセントラルによって影響を受けているため、エクスポートされた識別子にはcamelCaseを使用し、ローカルwhere- boundまたはletにはアンダースコア付きのshort_nameを使用することをお勧めしますバインド変数。

91
Norman Ramsey

私の経験則のいくつかの良いルール:

  • 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_などの関数のバンドルは非常に魅力的です。代わりにApplicativeスタイルでZipListsを使用してください。あなたはおそらく本当にそのような機能を本当に必要としないでしょう。
    • インスタンスを自動的に派生させます。 derive パッケージは、Functorなどの型クラスのインスタンスを導出するのに役立ちます(型をFunctorのインスタンスにする正しい方法は1つだけです)。
  • より一般的なコードにはいくつかの利点があります:
    • より便利で再利用可能です。
    • 制約が多いため、バグが発生しにくくなります。
      • たとえば、_concat :: [[a]] -> [a]_をプログラムし、join :: Monad m => m (m a) -> m aのように一般化できることに注意してください。 joinをプログラミングするとき、誤ってリストを逆にすることができ、concatで実行できることはほとんどないため、joinをプログラミングするときにエラーが発生する余地は少なくなります。
  • コードの多くの場所で同じスタックのモナド変換子を使用する場合は、型の同義語を作成します。これにより、型が短く、簡潔になり、一括で変更しやすくなります。
  • 「レイジーIO」に注意してください。たとえば、readFileは、ファイルが読み取られた時点では実際にはファイルの内容を読み取りません。
  • コードが見つからないほどインデントしないでください。
  • 型が論理的に型クラスのインスタンスである場合は、それをインスタンスにします。
    • インスタンスは、あなたがよく知っているものと考えたかもしれない他のインターフェース機能を置き換えることができます。
    • 注:複数の論理インスタンスがある場合は、インスタンスのnewtype-wrappersを作成します。
    • 異なるインスタンスに一貫性を持たせます。リストApplicativeZipListのように動作した場合、それは非常に混乱する/悪いことでした。
27
yairchu

これを見てみることをお勧めします スタイルチェッカー

6
  • 私は次のようなことを行うことで、可能な限りポイントフリースタイルのコンポジションとして機能を整理しようとしています。

    func = boo . boppity . bippity . snd
        where boo = ...
              boppity = ...
              bippity = ...
    
  • ネストされた括弧や長い括弧で囲まれた式を回避するためだけに($)を使用するのが好きです

  • ...私はもう少し自分の中にいると思った

6
jberryman

Haskellコードスタイルのほぼすべての側面をカバーする優れたマークダウンファイルを見つけました。チートシートとして使用できます。あなたはそれをここで見つけることができます: link

4
d12frosted