Perlベストプラクティスのベストプラクティスじゃないやつをまとめてみた
Perlベストプラクティス(略してPBP)という良い本があります。僕自身もPerlを学ぶ過程で非常にお世話になった本なのですが、以下の様なことが度々指摘されています。
- bestって書いてあるけど「著者の」bestプラクティスなので偏りがあることも
- 「決して」とか「必ず」とかが多いけどあんま真に受けてはいけない
- この本を書くために書かれたであろうCPANモジュールとかがあって、しかも公開されてないものまである
- 致し方ないけど現在の状況にマッチしない古い情報もある
なので、PBPの何がベストじゃないのかについてまとめてみることにした。前からやりたかったんだけど、思い立ってやった。
まとめてみたら、思っていたほどには項目が上がってこなかったので、やっぱPBPは良い本だなと改めて思いました。なので、このエントリーがこれからPBPを読む人の助けになれば良いなと思います。
色々意見ある人も多いと思うので突っ込みもお待ちしています。
2.9 行の長さ「78行の列を使用する」
根強い78幅信仰。こういうのは、ハードウェア事情に合わせて変わっていくものだと思っている。最近ディスプレイ幅あるし、120くらいまでいいんじゃないでしょうか。プロジェクトで軽くその辺は認識合わせをしておくのが良いかと。
2.10 インデント「4列のインデントレベルを使用する」
プロジェクトで統一すればいいと思う。最近2スペでもいいかなーとか思うことも。
2.14 Else「}
とelse
を同じ行に並べない」
僕もPBP読んでからそうしてますが、最近そうじゃない人多いし好みの問題。
2.16 長い文の分割「長い式は演算子の前で分割」
文中の分割方法は個人的に好みではないのでやってない。好みの問題。
2.22 自動レイアウト「選択したレイアウトスタイルを機械的に適用する」
perltidyは遅いしやってない。@moznion に期待。
3.3 参照変数「参照を保持する変数には_ref
サフィックスを付ける」
そんなハンガリアンはちょっと…。リファレンスが出てきたばかりで特別感があったころのやり方っぽい。
4.1 文字列デリミタ「文字列展開デリミタは、実際に展開する文字列にのみ使用する」
なるべくシングルクオートか、q[]
を使おうみたいな話。僕はそうしてるけど、あとから変数埋め込むときにめんどくさいから最初からダブルクオート使う派の人とかもいるので、どっちでもいいかなーとか思ってる。
4.2 空の文字列「空の文字列に""
または''
を使用しない」
空文字列にはq{}
を使え、みたいな話だけど、逆に見づらいので、''
で良いと思う。''
と"
を見間違えることが多いとか言ってるけど、そこにダブルクオートがあったら普通にシンタックスエラーだし。最近のフォント事情じゃ見間違えない。
4.5 定数「名前付きの定数を使用し、use constant
を使用しない」
普通にconstant
使いましょう。うまくすればコンパイルフェーズで余計な分岐削ってくれたりするのも嬉しい。Readonly
は遅い。
4.18 リストのメンバーシップ「文字列リストのメンバーシップの評価にはテーブル参照を使用し、その他のリストのメンバーシップにはany()
を使用する」
言ってることは分からんでもないけど、場合によりけりだし、だいぶ瑣末な指摘だと思う。
5.2 パッケージ変数「開発時にパッケージ変数を使用しない」
our
を使ったほうがデバッグに役立つ場合もあるので場合によりけり
5.5 句読点変数「あまり良く知られていない句読点変数には、use English
を使用する」
use English
しないで、コメントでフォローとかでいいと思う
5.12 スライスのリファクタリング「スライスのキーやインデックスの数が多い場合はリファクタリングする」
なんと、この節のコードサンプルは動きません!。Perl5.18以降に入ったハッシュランダマイゼーションが理由です。ハッシュスライスとか理解すると便利っぽいので多用したくなるけど、あとから自分のコード読んでもよく分からなかったりすることもあるので、節度が大事だと思います。
→ 動くっぽいという話です。要確認。
6.4 否定制御文「unless
またはuntil
を決して使用しない」
えっ。嫌いな人がいるのは知ってるけど「決して」はちょっと…。unless ... else
はダメだと思う。
8.1 ソート「ソート中にソートキーを再計算しない」
まーよく言われる。最近だと、List::UtilsBy
とかが便利です
8.8 ソートの自動化「Sort::Maker
によるソートルーチンの構築を検討する」
Sort::Maker
知らなかった。便利そうだけど結構ゴツイからちょっと微妙かもしれないし、List::UtilsBy
で解決する局面も多そう
9.4 名前付きの引数「サブルーチンの引数が3つ以上ある場合は、名前付きの引数のハッシュを使用する」
概ね同意だけど、判断基準は「3つ以上」ではないと思ってる。2つ以下の時も、その引数の順番に妥当性ががあるのであれば、名前付き引数にしなくて良いけど、単に偶然引数が2つ以下しか無いだけの場合は、後で増える可能性があるので、名前付きに予めしておいたほうが後々破綻しないと思う。
9.5 欠けている引数「引数が欠けているかどうかは、その定義または存在に基づいて評価する」
言ってることは間違ってない。ただ、どれくらい厳密にやるかは場合によりけりで、近年だと、引数の評価を厳密にやりたいんだったら、プロジェクトで統一的に、Data::Validator
なりSmart::Args
なりを使うと良いです。
9.6 引数のデフォルト値「@_
が展開されたらすぐに引数のデフォルト値を解決する」
言ってることは間違ってない。ただ、「||
演算子や||=
演算子を使用してでデフォルト値を選択すると…」とか書いてあるけど、別に気をつけて使えば良いんじゃないかと思うし、近年だと//
とか//=
とかあるので便利。あと、Data::ValidatorとかSmart::Argsとか(ry
9.7 スカラーコンテキストでの戻り値「スカラーを戻すときには、常にreturn scalar
を使用する」
厳しみがある。基本的にスカラーを返すことがほとんどなので、そのために余計な記述が増えるのは余り好きではない。そもそも余りコンテキストを意識させるようなコードを書かないほうが良いと思う。
9.8 コンテキストに応じた戻り値「リストを返すサブルーチンがスカラーコンテキストで「明白な」値を返すようにする」
コンテキストに応じて戻り値を変えるようなことはあんましないほうが良いんじゃないすかねー。リストを返したい場合もあるけど、そういう関数は、逆にスカラーで受けようとしていたら例外上げるくらいのほうが良いのかも。やってないけど。
9.9 マルチコンテキストでの戻り値「スカラーコンテキストでの「明白な」戻り値を絞り込めない場合には、Contexual::Returnを検討する」
コンテキストに応じて戻り値をかえるような(ry
9.10 プロトタイプ「サブルーチンプロトタイプを使用しない」
間違ってはいない。概ね使う必要ないけど、DSLとか書きたいときは使ったりもする。
9.12 失敗のリターン「失敗状態を返すには、裸のreturn
を使用する」
良いと思います。ただ僕の場合単にreturn
だけだとなんか正常終了っぽく感じるから、return ()
って空リスト返すようにしてる。けどこれもとある人に「キモイ」って言われたことあるので好みの問題。
10.10 強力な丸呑み「Perl6::Slurp
を使用して、ストリームを一気に丸呑みする」
最近だとFile::Slurp
なのかな?僕はPath::Tiny
でpath($file)->slurp_utf8
とかやってる。
10.12 ファイルハンドルへの出力「print
文ではファイルハンドルを常に中かっこで囲む」
やりたい人だけやればいい。$fh->print(...)
ってやるのは結構好き。
10.15 強力なプロンプト「プロンプトの表示にはIO::Prompt
モジュールを使用する」
IO::Prompt
は最近のPerlだとちゃんと動かないことがある悲しみ。同じダミアン先生作のIO::Prompter
ってのがあってそっちを使って欲しいみたいだけど、IO::Prompt::Simple
とかIO::Prompt::Tiny
とかを僕は使ってる。
12.1 拡張フォーマット「常に/x
フラグを使用する」
さすがに教条主義が過ぎるのではないかと多くの人に突っ込まれている項目。/x
は超便利ではあります。
12.2 行の境界「常に/m
フラグを使用する」
さすがに教条(ry
12.2 任意の文字とのマッチ「常に/s
フラグを使用する」
さすがに教条(ry 僕は複数行を意識する正規表現を書くときは/ms
はセットで指定するようにはしてます。
12.6 遅延フラグ「Regexp::Autoflags
モジュールの義務付けを検討する」
さすがに教条(ry
12.8 その他のデリミタ「/.../
またはm{...}
以外のデリミタをいっさい使用しない」
まあわかる。でも個人的にはm!!
使いたくなるときあるしそういうコードもちょいちょい見かけます。
13.2 組み込み関数のエラー「組み込み関数でエラーが発生した場合も、例外を送出させる」
それが良いと思います。ただ、文中にuse Fatal
への言及がありますが、最近だとuse autodie
ですね。
13.13 例外クラス「例外クラスを自動的に構築する」
途中から、Exception::Class
使えって話になってるけど、Exception::Tiny
とかも最近はいいと思います。
14.5 コマンドラインの処理「コマンドラインの処理を一つの方法に統一する」
文中に紹介されている、Getopt::Clade
がCPANに実装がなくてビビる。0.0.1しか上がってなくて、croak 'Getopt::Clade is not yet available';
とか書かれてるのヤバイ、闇っぽい。
文中で、Getopt::Euclid
の紹介とかもあるけど、未だにコマンドライン処理の決定版みたいなのって無いので好きなの使えば良いと思う。観測範囲では以下が使われている印象。
Getopt::Long
Getopt::Compact::WithCmd
App::Options
Smart::Options
Docopt
Getopt::Euclid
は知らなかったけど、Docopt
と似たような発想のモジュールのようだ。名前にキラキラ感があって良い。メンテナンスも続けられているようなので使ってみても良いかもしれない。
15.5 カプセル化「完全にカプセル化されたオブジェクトを使用する」
結局インサイドアウトオブジェクトは流行らずに、ほとんどがハッシュベースのオブジェクトが使われるようなって、それを前提にみんなコード書いてる。インサイドアウトはたまにテクニックとして使われることはある。
そんな躍起になってデストラクタにまで手を入れてカプセル化するよりかは、普通にやったほうがいいよねという感じだと思います。ハッシュベースのオブジェクトであっても、アクセサを介さずに直にハッシュキーを参照するのはお行儀が良くないとされているという落とし所。
15.10 アクセサ「読み取りアクセサと書き込みアクセサを別々に提供する」
古き良きセッターゲッター論争だけど、別々に提供してること少ないと思うし、実際別々に用意しなくていいと僕は思ってる。それよりオブジェクト生成後に書き込み可能なプロパティを減らすことを考えたほうが良いと思ってる。
15.15 型の強制変換「オブジェクトのブール値、数値、文字列値への強制型変換のオーバーロードを常に意識する」
演算子オーバーロードやり過ぎるとよくわからないことになるんで、やり過ぎ注意だと思う。あんまカジュアルに使うものではない。
あとoverload
の使い方だけど、文中の例のように無名関数を指定するんじゃなくて、文字列化だったらstringify
みたいなメソッドを定義しておいて、それを指定するのが定石。
use overload (
q{""} => 'stringify',
);
メソッド名がto_string
なのかstringify
なのかみたいなオーバーロードする上での統一的な指標がなくてバラバラなのが、Perlらしいといえばそうだけど、困りものだと思っている。むしろそういうガイドラインをPBPで示していれば良かったのではないかとか。
文中にあるような、変な型変換が入りそうになったら例外を上げるようにしておくってのは悪いアイデアではないし、そういうモジュールはあっても良いかもなとは思った。
16章 クラス階層
主にインサイドアウトオブジェクトとClass::Stdの説明なので、斜め読みで良い。
17.3 バージョン番号「3部構成のバージョン番号を使用する」
文中のqv
は今は非推奨です。Perlのモジュールバージョン宣言周りは色々紆余曲折があって、僕も良くわかってないのですが、現状3部構成のバージョン番号を使用したい場合は以下のようにするのが良いみたいです。
use version 0.77; our $VERSION = version->declare("v1.2.3"); # 必ず1行にする
詳しくは、version.pmのpodを参照のこと。3部構成に拘らないのなら、従来通りour $VERSION = '0.01';
でもいいと思います。
17.8 モジュールの作成「新しいモジュールフレームワークを自動的に作成する」
Module::Starterが紹介されていますが、最近だと、MinillaやDist::Zilla、それを利用したDist::Millaとかが使われることが多いです。僕はMinilla使ってます。