Masteries

技術的なことや仕事に関することを書いていきます.

チームでPerlを書く時に考えていること

id:papix です. この記事は, Perl Advent Calendar 2019の16日目の記事です. 昨日は bayashi_net さんの「今年書いた何か Github Actions」でした.

bayashi.net

さて, 今日は前回2日目に書いたキーワードプラグインについて, 実際の実装方法について解説する... と予告していましたが, 師走っているのと, XSについて記憶がかなり失われていることなどから, 準備に時間が足りませんでした. すいません...

papix.hatenablog.com

代わりに, 「チームでPerlを書く時に考えていること」というタイトルで, 自分自身が業務の中で, チームメンバーと一緒にPerlのプロダクトを開発していく中で, 気をつけていること, 考えていることについて綴ろうと思います.

注意

  • これはあくまで id:papix 個人の意見, 見解, 感想です
  • チームでPerlを書くにあたって, 必ず下記の点について気をつけないといけない, 守らないといけない! という訳ではありません

チームでPerlを書く時に考えていること

変数は宣言だけしない

変数を宣言するときに, 条件によって初期値を切り替えたい, と思うことがあると思います. そういう時の書き方として...

my $var;
if ($cond) {
    $var = 1;
} else {
    $var = 2;
}

このように書くことも出来ます. ただ最近, 上記のコードのように, 初期値の代入がない, ただの変数の宣言を見るとドキッとするようになりました. 理由はなんだろう? と考えると, 宣言した変数が一瞬でもundefである状態が怖いのかな? と思っています. その直後で正しく初期化しなければ, 意図しない挙動になってしまいそう... と思っているのかもしれません.

例えば, チームでプロダクトを開発していて, 様々な事情があって, my $var;という変数の宣言と, その変数の初期化の処理の間に, 更に別の処理が入っていって... と複雑化していくことはない... とは言い切れないと思います.

my $var;

... 他のいろいろなコード ...

# ようやくここで $var を初期化する
if ($cond) {
    $var = 1;
} else {
    $var = 2;
}

こういう時に, もし間違って ... 他のいろいろなコード ... の中で $var を使ってしまうと, 当然初期化の処理が実行されていないので意図しない挙動になります. 一方で, $var は変数として宣言されているので構文としては正しく, 結果としては「なぜundefなんだろう...?」と悩むことになります.

...というわけで, 最近は変数の宣言と初期値の代入は必ず同時にするように心がけています. 例えば, 先の例であれば次のように書きます:

my $var = $cond ? 1 : 0;

条件が複雑な場合は, 次のようにdoを使ったりもします. doの場合, スコープが区切られるので初期値を生成するために一時的に変数を宣言する, といったこともできます.

my $var = do {
   my $cond = ...;
   if ($cond == 1) {
      'a';
   } elsif ($cond == 2 ) {
      'b';
   } else {
      'c';
   }
};

あるいは, 初期値を生成するためのメソッドを用意するのも良いでしょう:

my $var = _prepare_var(...);

importするメソッドは明記するようにする

Perlには, import/exportという仕組みがあります. 例えば, 次のようなSampleパッケージがあるとしましょう. このコードでは, Exporterモジュールを使って, func_a, func_bをエクスポートしています.

package Sample;

use Exporter 'import';
our @EXPORT = qw(func_a func_b);

sub func_a { ... }
sub func_b { ... }

1;

こうすることで, our @EXPORT に含まれる名前のメソッドは, そのパッケージをインポートするだけで自動的に利用出来るようになります.

use Sample;

func_a(); # => Sample#func_a が呼べる
Sample::func_a(); # import/exportの仕組みを使わないなら, こう書かないといけない

func_b(); # => Sample#func_b も呼べる

import/exportの仕組みはとても便利です. ただ, チームでPerlを開発していて, 次のようなコードに手を加えることになったら... どう思いますか?

use Sample1;
use Sample2;
use Sample3;
use Sample4;
use Sample5;

func_a();

「...func_aってどこでexportされているんだ?」と困惑してしまいそうです. そこで, チームでPerlを書くような場合は, importするメソッドを次のように明記するようにしています.

use Sample1 qw(func_a);
use Sample2;
use Sample3;
use Sample4;
use Sample5;

func_a();

このコードのように, useでパッケージをimportする際, 文字列で関数名を渡すと, その名前の関数だけをインポートすることができます. こうすれば, 「なるほど, func_aSample1にあるんだな!」と一目瞭然ですね.

use Sample1 qw();

func_a(); # => 呼べない
Sample1::func_a(); # => 呼べる

ちなみに, 上記のコードのように, use Sample1 qw(); としてモジュールを呼び出すと, 全ての関数をインポートしないように出来ます. この場合, パッケージ名を付与して関数を呼び出してあげることになります

さいごに

チームでPerlを書く時に気をつけていることをご紹介しました. Perlは超手軽につかえて便利なプログラミング言語ですが, とはいえチーム開発というシーンにおいてはその手軽さに甘えた時に落とし穴にハマる, みたいなこともあるので, 意識するべきところを意識して書いていきたいものです. ところでこのエントリは時間の制約上かなりシュッと書いたので, もっといろいろ気をつけポイントはあると思うので, 「○○○はどうですか?」とか, 「✕✕✕も罠になるので気をつけると良さそう」といった感想をお待ちしています.

Perl Advent Calendar 2019, 明日の担当は, doikojiさんです.