アプリケーション開発をしていて、種別ごとに処理が分岐されているコードはよく見ると思います。 例えば、次のコードは自然言語の種別ごとに挨拶を返す関数です。
use v5.40; sub hello ($lang) { if ($lang eq 'JP') { say 'こんにちは'; } elsif ($lang eq 'EN') { say 'Hello'; } else { die 'Unknown language'; } }
この挨拶コードで、もしフランス語も対応することになったら、どうしましょうか?
$lang eq 'FR'の分岐を追加すれば良いでしょうか。
こういった種別による対応箇所が少なく自明であれば問題は起きにくいかもしれないですが、
種別による分岐がアプリケーションコードの至るところにあり、コードが肥大化していた場合、種別の追加漏れで不具合が発生しがちです。
しかも、事前に問題は気づきにくいです。
静的型付け言語であれば、例えば、never型を利用して、コンパイル時にエラーを検知できます。

では、動的型付け言語のPerlの場合はどうすれば良いでしょうか?ここでは2つのアプローチを書きます。
1. 全種別で自動テストを回す
非常に素朴なアプローチですが、全種別で正常動作することを確認するテストを書けば、種別の追加漏れを事前に気づける可能性は上がります。
use Test2::V0; use constant LANG => ['JP','EN']; subtest '全種別でhelloが動く' => sub { for my $lang (LANG->@*) { ok lives { hello($lang) }; } };
単純な方法ですし、応用力もあります。ただ、実行したい関数の事前準備が多い場合、手間もあるため、個人的には次のアプローチも手札にいれることをおすすめします。
2. マッピング漏れがないかAssertionする
2つ目のアプローチは、種別の違いをマッピングし、これをAssertionする方法です。このアプローチの場合、自動テストで全種別で回す必要はなく、assertを通りさえすれば良いです。この方法でも網羅性チェックができます。
use Syntax::Keyword::Assert; use constant LANG => ['JP','EN']; sub check($mapping, $keys) { return !grep { !exists $mapping->{$_} } $keys->@*; } sub hello ($lang) { state $mapping = { JP => 'こんにちは', EN => 'Hello', }; assert(check($mapping, LANG)); $mapping->{$lang} }
余談ですが、これまでのPerlのAssertionライブラリの場合、パフォーマンスに影響を与えることがありますが、このSyntax::Keyword::Assertは、コンパイル時にAssertionを削除するため、パフォーマンスに影響を与えません。
まとめ
Perlで網羅性チェックのために、全種別で自動テストを回すアプローチとマッピング漏れをAssertionするアプローチを紹介しました。
以上です!