clojure.lib コーディング規約・訳
In: Clojure
4
2月
2010
2/13 – 更新された規約はこちらの記事にまとめました。
⇒
“Clojureライブラリ・コーディング規約” まとめ
Clojure Dev Google groupsで盛り上がっているトピックがあります。
“clojure.lib coding standards: initial draft brain dump Options”
Programming Clojure の著者、Stuart Halloway氏が音頭を取ってclojure.lib用のコーディング規約をみんなで決めようというものです。
興味深かったのでStuの提案したルールを和訳してみました。
思いっきり意訳です。間違いがあればご指摘ねがいます。
⇒ @manjilab
(掲示板の流れに合わせていくつかの項目を追加・削除しています)
【和訳ここから】
最初に:
- これは公式でも最終版でもない。議論を誘発するための叩き台です。もし致命的に間違っている項目が含まれていなければ私の手抜きです :-)
- ルールは破られるためにある。よってどのような規約も絶対ではない。
- 有言実行:ここで同意が得られた項目に沿うように自分の既存のコードを書き直すつもりです。
- この文章はClojureの入門者にとって役立つものになるでしょう。参考資料となるリンクも示してあります。
規約:
- 名前と使用法はよく考えて書きましょう。RichはJavaにおける既存のコードとの互換性の維持を尊重しています。練習用のコードであればいつまでもいじってられますが、ひとたび名前と使用法が公開されればそうはいきません。(具体的な実装に興味がなく名前と用法だけを見ている利用者が多いですから)
- パフォーマンス的に重要な部分には type hintをつける。
- 良い名前をつける。他の名前空間のものとの衝突を恐れない。そのための名前空間。
- パッケージ依存は明確・最小限に。(useやrequireをする時には :onlyオプションを使う)
- 関数で実現できる場合はマクロは使わない。マクロの方が使い良い場合は関数版も提供すること。(みんなへ質問:命名規則は必要? “foo” と “foo*”が多く使われてるけど)
- コンパイル時にすべての情報がわかっているのであれば、パフォーマンス上有効な部分にマクロを使う。(
ログのためのマクロ
の議論を参考。結局両方のバージョンが必要となった。コンパイル時に完全な情報が得られないケースが多いからだ。)
- ライブラリレベルで docstringを書くこと。
- 全関数に対して自動化されたテストを書くこと。
- オプションの引数は展開すること。
(foo x y {:optional z}) ではなく (foo x y :optional z)
- 述語(true または falseを返す)関数名の末尾は ‘?’ で。
- docstringを付けよ。例外として、関数がの定義が明白そのものでdocstringが関数名と引数をただ冗長に示すだけのような場合は書かない。(私もそれに該当するdocstringは削除する方針にした。)
- 迷ったら性能のよい方を公開すること。Clojureは求められるパフォーマンスを提供すべきであり、ライブラリも同じです。(それがマルチメソッド版の + をcoreでは提供していない理由です。)ユーザは必要に応じてAPIをハイジャックして新たな型を作ることが出来るのですから。
- よい名前を使いたいけどcoreと衝突するような場合は意味が同等になるように心がけてください。coreのシーケンス関数に対する文字列処理関数が良い例となるでしょう。
- assertや pre- post-などの条件処理を気軽に使いましょう。現時点ではあまり使われていませんが、もっと使われるべきです。
参考
- できるだけ遅延評価で書きましょう。
- 変数名は pred や coll のように clojure.coreの慣例に従うこと。
- clojure.coreの起動初期コードの慣用表記に倣ってはいけない。それらはClojureが立ち上がる前の限定された環境のために書かれているから。
- コードを小さい塊に分割せよ。doseqの定義のような書式はRich以外の人は書かないで。
- マップ要素へのアクセスは (:property object-like-map) 、コレクションへは (collection-like-map key) を使う。もしくは get を使う。(追加)
- 分配束縛はよく用いられる。しかし渡された値の一部を使いたいときには引数書式の所での使用にどめるべきだ。もしくはletの最初の行で。でも
Programming Clojureに載せたのスネークゲームのコード
はこれに反しているんだけどね。
- (トランザクション系の話)セットよりも更新を使おう。理由はたくさんある。統一されたupdateモデルは簡潔で標準的であるので、交換可能な処理を発見しやすくなる。それにより更新しようとしている処理の衝突を少なくできる。
- 想定していないコレクション型をサポートしてはいけない。アルゴリズムがランダムアクセスを用いるのであれば、受け取る型はランダムアクセスできるものでなければならない。
- *earmuffs* は再束縛を意図するものにのみ使用する。
- bang! はSTMトランザクションで安全でないものにのみ使用する。
- loop/recurよりもシーケンス・ライブラリの組み合わせを用いる。
- varの再束縛にはスコープ系マクロをいっしょに。例)*in* と with-in-str
- 遅延シーケンスは最小限の状態を持つ関数として書く。つまり「頭をかかえない」こと。どの程度の規模で現実化するかは呼び出し側にまかせること。
- interop は (Klass/static) , (Klass.) , (.instance obj) の型式で。
例外はコードを生成するコード中で(. blah)の方が簡単な場合のみ。
- 引数の順序はよく考えて。ライブラリのなかでは一貫性を持たせること。
【和訳ここまで】
加筆
【2010.2.4 – 10:30】
・『マップ要素へのアクセスは (:property object-like-map)〜』を追加
・bang! の項:「安全でないSTMトランザクション」 ⇒ 「STMトランザクションで安全でないもの」
@athos0220さん、ご指摘ありがとうございました。