Communitygithub.com

perl-patterns

モダンなPerl 5.36+のイディオム、ベストプラクティス、コーディング規約。堅牢でメンテナンスしやすいPerlアプリケーションを構築・レビューする際に使用します。

perl-patterns とは?

このスキルは、Perl 5.36以降を使用する開発者向けに設計された、最新のコーディングパターンとベストプラクティスを集約したものです。特に、use v5.36プラグマによるボイラープレート削減、サブルーチンシグネチャ、後置デリファレンス、isa演算子などの言語機能を活用し、堅牢で読みやすいコードを書くための具体的な指針を提供します。対応プラットフォームはClaude Code、Cursor、Codexなど、AIエージェントがコード生成やレビューを行うあらゆる環境を想定しています。スキル内容は、エラーハンドリング(Try::Tinyやネイティブtry/catch)、Mooを用いたモダンなオブジェクト指向、名前付きキャプチャを使った正規表現、Path::Tinyによるファイル操作、cpanfileによる依存管理など、実践的なトピックを網羅しています。また、perltidyやperlcriticの設定例、アンチパターンの解説も含まれており、既存コードのモダン化や新規プロジェクトの品質向上に役立ちます。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/perl-patterns

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

モダンPerl開発パターン

Perl 5.36+のイディオムとベストプラクティス。堅牢でメンテナンスしやすいアプリケーションを構築するためのガイドです。

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

  • 新しいPerlコードやモジュールを書くとき
  • Perlコードのイディオム準拠をレビューするとき
  • レガシーPerlコードをモダンスタンダードにリファクタリングするとき
  • Perlモジュールアーキテクチャを設計するとき
  • 5.36未満のコードをモダンPerlに移行するとき

動作の仕組み

これらのパターンを、モダンPerl 5.36+のデフォルト(シグネチャ、明示的なモジュール、集中的なエラーハンドリング、テスト可能な境界)に偏らせる形で適用します。以下の例は出発点としてコピーし、実際のアプリ、依存スタック、デプロイモデルに合わせて調整してください。

中核となる原則

1. use v5.36 プラグマ

単一の use v5.36 で、従来のボイラープレートを置き換え、strict、warnings、サブルーチンシグネチャを有効にします。

# 良い: モダンなプリアンブル
use v5.36;

sub greet($name) {
    say "Hello, $name!";
}

# 悪い: レガシーなボイラープレート
use strict;
use warnings;
use feature 'say', 'signatures';
no warnings 'experimental::signatures';

sub greet {
    my ($name) = @_;
    say "Hello, $name!";
}

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

シグネチャを使用してコードの明確さと自動的な引数チェックを実現します。

use v5.36;

# 良い: デフォルト値付きシグネチャ
sub connect_db($host, $port = 5432, $timeout = 30) {
    # $host は必須、他はデフォルト値あり
    return DBI->connect("dbi:Pg:host=$host;port=$port", undef, undef, {
        RaiseError => 1,
        PrintError => 0,
    });
}

# 良い: 可変引数のためのスラーピーパラメータ
sub log_message($level, @details) {
    say "[$level] " . join(' ', @details);
}

# 悪い: 手動での引数アンパック
sub connect_db {
    my ($host, $port, $timeout) = @_;
    $port    //= 5432;
    $timeout //= 30;
    # ...
}

3. コンテキストの重要性

スカラコンテキストとリストコンテキストの違いを理解する ― Perlの重要な概念です。

use v5.36;

my @items = (1, 2, 3, 4, 5);

my @copy  = @items;            # リストコンテキスト: すべての要素
my $count = @items;            # スカラコンテキスト: 要素数 (5)
say "Items: " . scalar @items; # スカラコンテキストを強制

4. 後置デリファレンス

入れ子構造で読みやすさを向上させるために、後置デリファレンス構文を使用します。

use v5.36;

my $data = {
    users => [
        { name => 'Alice', roles => ['admin', 'user'] },
        { name => 'Bob',   roles => ['user'] },
    ],
};

# 良い: 後置デリファレンス
my @users = $data->{users}->@*;
my @roles = $data->{users}[0]{roles}->@*;
my %first = $data->{users}[0]->%*;

# 悪い: 周囲型デリファレンス(チェーンでは読みにくい)
my @users = @{ $data->{users} };
my @roles = @{ $data->{users}[0]{roles} };

5. isa 演算子 (5.32+)

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

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

エラーハンドリング

eval/die パターン

use v5.36;

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

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

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+)

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にはMooを推奨します。メタプロトコルが必要な場合のみMooseを使用してください。

# 良い: 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'],
);

# 悪い: blessedハッシュリファレンス(バリデーションなし、アクセサなし)
package User;
sub new {
    my ($class, %args) = @_;
    return bless \%args, $class;
}
sub name { return $_[0]->{name} }
1;

Mooロール

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)

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 フラグ

use v5.36;

# 良い: 読みやすさのための名前付きキャプチャと /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}";
}

# 悪い: 位置ベースのキャプチャ(保守が難しい)
if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+\[(\w+)\]\s+(.+)$/) {
    say "Time: $1, Level: $2";
}

プリコンパイル済みパターン

use v5.36;

# 良い: 一度コンパイルして何度も使用
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;
}

データ構造

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

use v5.36;

# ハッシュと配列リファレンス
my $config = {
    database => {
        host => 'localhost',
        port => 5432,
        options => ['utf8', 'sslmode=require'],
    },
};

# 安全な深いアクセス(どのレベルでも欠けていれば undef を返す)
my $port = $config->{database}{port};           # 5432
my $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

三引数open

use v5.36;

# 良い: autodie を使った三引数open(コアモジュール、'or die'を不要にする)
use autodie;

sub read_file($path) {
    open my $fh, '<:encoding(UTF-8)', $path;
    local $/;
    my $content = <$fh>;
    close $fh;
    return $content;
}

# 悪い: 二引数open(シェルインジェクションのリスクあり、perl-security参照)
open FH, $path;            # 絶対にやらないでください
open FH, "< $path";        # これもダメ ― モード文字列にユーザーデータが入る

Path::Tiny によるファイル操作

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;
}

モジュール構成

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

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

Exporter パターン

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)

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

perlcritic 設定 (.perlcriticrc)

severity = 3
theme = core + pbp + security

[InputOutput::RequireCheckedSyscalls]
functions = :builtins
exclude_functions = say print

[Subroutines::ProhibitExplicitReturnUndef]
severity = 4

[ValuesAndExpressions::ProhibitMagicNumbers]
allowed_values = 0 1 2 -1

依存関係管理 (cpanfile + carton)

cpanm App::cpanminus Carton   # ツールのインストール
carton install                 # cpanfile から依存をインストール
carton exec -- perl bin/myapp  # ローカルの依存で実行
# cpanfile
requires '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 ハッシュリファレンス型付き 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+, 実験的)

アンチパターン

# 1. 二引数open(セキュリティリスク)
open FH, $filename;                     # 絶対にやらないでください

# 2. 間接オブジェクト構文(解析が曖昧)
my $obj = new Foo(bar => 1);            # 悪い
my $obj = Foo->new(bar => 1);           # 良い

# 3. $_ への過度な依存
map { process($_) } grep { validate($_) } @items;  # 追いにくい
my @valid = grep { validate($_) } @items;           # より良い: 分割する
my @results = map { process($_) } @valid;

# 4. strict refs の無効化
no strict 'refs';                        # ほとんどの場合間違い
${"My::Package::$var"} = $value;         # 代わりにハッシュを使用

# 5. 設定としてのグローバル変数
our $TIMEOUT = 30;                       # 悪い: 可変なグローバル
use constant TIMEOUT => 30;              # より良い: 定数
# 最良: デフォルト値を持つMooアトリビュート

# 6. モジュール読み込みのための文字列eval
eval "require $module";                  # 悪い: コードインジェクションのリスク
eval "use $module";                      # 悪い
use Module::Runtime 'require_module';    # 良い: 安全なモジュール読み込み
require_module($module);

覚えておいてください: モダンPerlは、クリーンで読みやすく、安全です。use v5.36 にボイラープレートを任せ、オブジェクトにはMooを使い、手作りの解決策よりもCPANの実戦で鍛えられたモジュールを優先しましょう。

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

関連スキル