Perl Patterns

by affaan-mef648e01899bNo license275K starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 3 days ago

堅牢でメンテナブルなPerlアプリケーションを構築するためのModern Perl 5.36+のイディオム、ベストプラクティス、規約。

Instructions onlySoftware Development
AI-generated overview

Reference guidance for writing, reviewing and refactoring Modern Perl 5.36+ code using idiomatic patterns and best practices.

What it does
Provides a set of Modern Perl 5.36+ idioms, conventions and best practices covering pragmas, subroutine signatures, context, postfix dereferencing, error handling, Moo-based OO, regexes, data structures, file I/O, module layout and tooling configuration. It includes code examples contrasting good and bad patterns, a quick-reference table mapping legacy patterns to modern replacements, and a list of anti-patterns. It produces guidance and example code rather than executable artifacts.
When to use it
Use it when writing new Perl code or modules, reviewing Perl for idiom compliance, refactoring legacy Perl to modern standards, designing Perl module architecture, or migrating pre-5.36 code to Modern Perl.
Requirements
No scripts are shipped; it is instructions and examples only. Applying the examples assumes a Perl 5.36+ runtime and, for some patterns, CPAN modules such as Moo, Try::Tiny, Path::Tiny, JSON::MaybeXS, Exporter, autodie, Module::Runtime, plus tooling like perltidy, perlcritic and carton.

モダンPerl開発パターン

堅牢でメンテナブルなアプリケーションを構築するためのイディオマティックなPerl 5.36+パターンとベストプラクティス。

アクティベートするタイミング

  • 新しいPerlコードまたはモジュールを書くとき
  • イディオム準拠のためにPerlコードをレビューするとき
  • レガシーPerlをモダンな標準にリファクタリングするとき
  • PerlモジュールのアーキテクチャをDesignするとき
  • 5.36以前のコードをモダンなPerlに移行するとき

仕組み

これらのパターンをModern Perl 5.36+のデフォルトへのバイアスとして適用する: シグネチャ、明示的なモジュール、集中的なエラー処理、テスト可能な境界。以下の例は出発点としてコピーし、目の前の実際のアプリ、依存スタック、デプロイモデルに合わせて締め付けることを意図している。

コア原則

1. v5.36プラグマの使用

単一のuse v5.36が古い定型文を置き換え、strict、warnings、サブルーチンシグネチャを有効化する。

perl
# Good: モダンなプリアンブルuse v5.36;
sub greet($name) {    say "Hello, $name!";}
# Bad: レガシーな定型文use strict;use warnings;use feature 'say', 'signatures';no warnings 'experimental::signatures';
sub greet {    my ($name) = @_;    say "Hello, $name!";}

2. サブルーチンシグネチャ

明確さと自動アリティチェックのためにシグネチャを使用する。

perl
use v5.36;
# Good: デフォルト値付きシグネチャsub connect_db($host, $port = 5432, $timeout = 30) {    # $hostは必須、その他はデフォルトあり    return DBI->connect("dbi:Pg:host=$host;port=$port", undef, undef, {        RaiseError => 1,        PrintError => 0,    });}
# Good: 可変引数のためのスラーピーパラメータsub log_message($level, @details) {    say "[$level] " . join(' ', @details);}
# Bad: 手動引数アンパックsub connect_db {    my ($host, $port, $timeout) = @_;    $port    //= 5432;    $timeout //= 30;    # ...}

3. コンテキスト感度

スカラーvsリストコンテキストを理解する — Perlのコアコンセプト。

perl
use v5.36;
my @items = (1, 2, 3, 4, 5);
my @copy  = @items;            # リストコンテキスト: すべての要素my $count = @items;            # スカラーコンテキスト: カウント (5)say "Items: " . scalar @items; # スカラーコンテキストを強制

4. 後置逆参照

ネストされた構造で読みやすさのために後置逆参照構文を使用する。

perl
use v5.36;
my $data = {    users => [        { name => 'Alice', roles => ['admin', 'user'] },        { name => 'Bob',   roles => ['user'] },    ],};
# Good: 後置逆参照my @users = $data->{users}->@*;my @roles = $data->{users}[0]{roles}->@*;my %first = $data->{users}[0]->%*;
# Bad: 前置逆参照(チェーンで読みにくい)my @users = @{ $data->{users} };my @roles = @{ $data->{users}[0]{roles} };

5. isa演算子(5.32+)

中置型チェック — blessed($o) && $o->isa('X')を置き換える。

perl
use v5.36;if ($obj isa 'My::Class') { $obj->do_something }

エラー処理

eval/dieパターン

perl
use v5.36;
sub parse_config($path) {    my $content = eval { path($path)->slurp_utf8 };    die "Config error: $@" if $@;    return decode_json($content);}

Try::Tiny(信頼性の高い例外処理)

perl
use v5.36;use Try::Tiny;
sub fetch_user($id) {    my $user = try {        $db->resultset('User')->find($id)            // die "User $id not found\n";    }    catch {        warn "Failed to fetch user $id: $_";        undef;    };    return $user;}

ネイティブtry/catch(5.40+)

perl
use v5.40;
sub divide($x, $y) {    try {        die "Division by zero" if $y == 0;        return $x / $y;    }    catch ($e) {        warn "Error: $e";        return;    }}

MooによるモダンOO

軽量でモダンなOOにはMooを優先する。メタプロトコルが必要な場合のみMooseを使用する。

perl
# Good: Mooクラスpackage User;use Moo;use Types::Standard qw(Str Int ArrayRef);use namespace::autoclean;
has name  => (is => 'ro', isa => Str, required => 1);has email => (is => 'ro', isa => Str, required => 1);has age   => (is => 'ro', isa => Int, default  => sub { 0 });has roles => (is => 'ro', isa => ArrayRef[Str], default => sub { [] });
sub is_admin($self) {    return grep { $_ eq 'admin' } $self->roles->@*;}
sub greet($self) {    return "Hello, I'm " . $self->name;}
1;
# 使用法my $user = User->new(    name  => 'Alice',    email => '[email protected]',    roles => ['admin', 'user'],);
# Bad: ブレスされたhashref(バリデーションなし、アクセサなし)package User;sub new {    my ($class, %args) = @_;    return bless \%args, $class;}sub name { return $_[0]->{name} }1;

Mooロール

perl
package Role::Serializable;use Moo::Role;use JSON::MaybeXS qw(encode_json);requires 'TO_HASH';sub to_json($self) { encode_json($self->TO_HASH) }1;
package User;use Moo;with 'Role::Serializable';has name  => (is => 'ro', required => 1);has email => (is => 'ro', required => 1);sub TO_HASH($self) { { name => $self->name, email => $self->email } }1;

ネイティブclassキーワード(5.38+、Corinna)

perl
use v5.38;use feature 'class';no warnings 'experimental::class';
class Point {    field $x :param;    field $y :param;    method magnitude() { sqrt($x**2 + $y**2) }}
my $p = Point->new(x => 3, y => 4);say $p->magnitude;  # 5

正規表現

名前付きキャプチャと/xフラグ

perl
use v5.36;
# Good: 読みやすさのための/xを使った名前付きキャプチャmy $log_re = qr{    ^ (?<timestamp> \d{4}-\d{2}-\d{2} \s \d{2}:\d{2}:\d{2} )    \s+ \[ (?<level> \w+ ) \]    \s+ (?<message> .+ ) $}x;
if ($line =~ $log_re) {    say "Time: $+{timestamp}, Level: $+{level}";    say "Message: $+{message}";}
# Bad: 位置キャプチャ(メンテが難しい)if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+\[(\w+)\]\s+(.+)$/) {    say "Time: $1, Level: $2";}

プリコンパイルパターン

perl
use v5.36;
# Good: 1回コンパイル、複数回使用my $email_re = qr/^[A-Za-z0-9._%+-]+\@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
sub validate_emails(@emails) {    return grep { $_ =~ $email_re } @emails;}

データ構造

リファレンスと安全な深いアクセス

perl
use v5.36;
# ハッシュと配列リファレンスmy $config = {    database => {        host => 'localhost',        port => 5432,        options => ['utf8', 'sslmode=require'],    },};
# 安全な深いアクセス(どのレベルが欠落してもundefを返す)my $port = $config->{database}{port};           # 5432my $missing = $config->{cache}{host};           # undef、エラーなし
# ハッシュスライスmy %subset;@subset{qw(host port)} = @{$config->{database}}{qw(host port)};
# 配列スライスmy @first_two = $config->{database}{options}->@[0, 1];
# 複数変数のforループ(5.36で実験的、5.40で安定)use feature 'for_list';no warnings 'experimental::for_list';for my ($key, $val) (%$config) {    say "$key => $val";}

ファイルI/O

3引数open

perl
use v5.36;
# Good: autodieを使った3引数open(コアモジュール、'or die'を排除)use autodie;
sub read_file($path) {    open my $fh, '<:encoding(UTF-8)', $path;    local $/;    my $content = <$fh>;    close $fh;    return $content;}
# Bad: 2引数open(シェルインジェクションリスク、perl-securityを参照)open FH, $path;            # 絶対にしないopen FH, "< $path";        # まだ悪い — モード文字列のユーザーデータ

ファイル操作のPath::Tiny

perl
use v5.36;use Path::Tiny;
my $file = path('config', 'app.json');my $content = $file->slurp_utf8;$file->spew_utf8($new_content);
# ディレクトリを反復for my $child (path('src')->children(qr/\.pl$/)) {    say $child->basename;}

モジュール構成

標準プロジェクトレイアウト

text
MyApp/├── lib/│   └── MyApp/│       ├── App.pm           # メインモジュール│       ├── Config.pm        # 設定│       ├── DB.pm            # データベース層│       └── Util.pm          # ユーティリティ├── bin/│   └── myapp                # エントリーポイントスクリプト├── t/│   ├── 00-load.t            # コンパイルテスト│   ├── unit/                # ユニットテスト│   └── integration/         # インテグレーションテスト├── cpanfile                 # 依存関係├── Makefile.PL              # ビルドシステム└── .perlcriticrc            # リンティング設定

エクスポーターパターン

perl
package MyApp::Util;use v5.36;use Exporter 'import';
our @EXPORT_OK   = qw(trim);our %EXPORT_TAGS = (all => \@EXPORT_OK);
sub trim($str) { $str =~ s/^\s+|\s+$//gr }
1;

ツーリング

perltidy設定(.perltidyrc)

text
-i=4        # 4スペースインデント-l=100      # 100文字行長-ci=4       # 継続インデント-ce         # cuddled else-bar        # 同じ行に開き括弧-nolq       # 長い引用文字列のアウトデントをしない

perlcritic設定(.perlcriticrc)

ini
severity = 3theme = core + pbp + security
[InputOutput::RequireCheckedSyscalls]functions = :builtinsexclude_functions = say print
[Subroutines::ProhibitExplicitReturnUndef]severity = 4
[ValuesAndExpressions::ProhibitMagicNumbers]allowed_values = 0 1 2 -1

依存関係管理(cpanfile + carton)

bash
cpanm App::cpanminus Carton   # ツールをインストールcarton install                 # cpanfileから依存関係をインストールcarton exec -- perl bin/myapp  # ローカル依存関係で実行
perl
# cpanfilerequires 'Moo', '>= 2.005';requires 'Path::Tiny';requires 'JSON::MaybeXS';requires 'Try::Tiny';
on test => sub {    requires 'Test2::V0';    requires 'Test::MockModule';};

クイックリファレンス: モダンPerlイディオム

レガシーパターンモダンな置き換え
use strict; use warnings;use v5.36;
my ($x, $y) = @_;sub foo($x, $y) { ... }
@{ $ref }$ref->@*
%{ $ref }$ref->%*
open FH, "< $file"open my $fh, '<:encoding(UTF-8)', $file
blessed hashref型付きのMooクラス
$1, $2, $3$+{name}(名前付きキャプチャ)
eval { }; if ($@)Try::Tinyまたはネイティブtry/catch(5.40+)
BEGIN { require Exporter; }use Exporter 'import';
手動ファイル操作Path::Tiny
blessed($o) && $o->isa('X')$o isa 'X'(5.32+)
builtin::true / falseuse builtin 'true', 'false';(5.36+、実験的)

アンチパターン

perl
# 1. 2引数open(セキュリティリスク)open FH, $filename;                     # 絶対にしない
# 2. 間接オブジェクト構文(あいまいな解析)my $obj = new Foo(bar => 1);            # Badmy $obj = Foo->new(bar => 1);           # Good
# 3. $_への過度の依存map { process($_) } grep { validate($_) } @items;  # 追うのが難しいmy @valid = grep { validate($_) } @items;           # より良い: 分割するmy @results = map { process($_) } @valid;
# 4. strictリファレンスの無効化no strict 'refs';                        # ほぼ常に間違い${"My::Package::$var"} = $value;         # 代わりにhashを使用
# 5. グローバル変数を設定として使用our $TIMEOUT = 30;                       # Bad: 可変グローバルuse constant TIMEOUT => 30;              # Better: 定数# Best: デフォルト付きのMoo属性
# 6. モジュールロードのための文字列evaleval "require $module";                  # Bad: コードインジェクションリスクeval "use $module";                      # Baduse Module::Runtime 'require_module';    # Good: 安全なモジュールロードrequire_module($module);

忘れないこと: モダンなPerlはクリーン、読みやすく、安全である。use v5.36に定型文を処理させ、オブジェクトにはMooを使用し、手作りのソリューションよりCPANの実績あるモジュールを優先する。

Source and attribution

Source:affaan-m/eccindocs/ja-JP/skills/perl-patternsat commitef648e0

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal