Perl Patterns

affaan-m/ECC/skills/perl-patterns

作者 affaan-mef648e01899ba3e8dc6371642deaaf64b4477775無授權條款275K 個星標收錄於 2026年10月9日更新於 2026年10月9日儲存庫4 天前更新

Modern Perl 5.36+ idioms, best practices, and conventions for building robust, maintainable Perl applications. Use when writing or reviewing modern Perl 5.36+ code.

AI 產生的概覽

現代 Perl 5.36+ 慣用語參考指南,涵蓋語法、錯誤處理、物件導向、正規表示式、檔案 I/O 與工具鏈。

功能
提供 Perl 5.36+ 的慣用模式與最佳實務,用於撰寫與審查可維護的 Perl 應用程式。內容涵蓋 pragma 與簽章、錯誤處理、Moo 與原生 class 物件導向、正規表示式、資料結構、檔案 I/O、模組組織、工具設定以及反模式。這是一份僅含說明的參考,附有可複製並調整的程式碼範例。
適用情境
適用於撰寫新的 Perl 程式碼或模組、審查 Perl 程式碼是否符合慣用語、重構舊有 Perl、設計模組架構,或將 5.36 之前的程式碼遷移到現代 Perl。
執行需求
除代理程式外無需其他條件;僅為說明,不含指令碼。範例涉及 Moo、Path::Tiny、Try::Tiny、JSON::MaybeXS 等 CPAN 模組,但閱讀這些指引無需安裝任何項目。

Modern Perl Development Patterns

Idiomatic Perl 5.36+ patterns and best practices for building robust, maintainable applications.

When to Activate

  • Writing new Perl code or modules
  • Reviewing Perl code for idiom compliance
  • Refactoring legacy Perl to modern standards
  • Designing Perl module architecture
  • Migrating pre-5.36 code to modern Perl

How It Works

Apply these patterns as a bias toward modern Perl 5.36+ defaults: signatures, explicit modules, focused error handling, and testable boundaries. The examples below are meant to be copied as starting points, then tightened for the actual app, dependency stack, and deployment model in front of you.

Core Principles

1. Use v5.36 Pragma

A single use v5.36 replaces the old boilerplate and enables strict, warnings, and subroutine signatures.

perl
# Good: Modern preambleuse v5.36;
sub greet($name) {    say "Hello, $name!";}
# Bad: Legacy boilerplateuse strict;use warnings;use feature 'say', 'signatures';no warnings 'experimental::signatures';
sub greet {    my ($name) = @_;    say "Hello, $name!";}

2. Subroutine Signatures

Use signatures for clarity and automatic arity checking.

perl
use v5.36;
# Good: Signatures with defaultssub connect_db($host, $port = 5432, $timeout = 30) {    # $host is required, others have defaults    return DBI->connect("dbi:Pg:host=$host;port=$port", undef, undef, {        RaiseError => 1,        PrintError => 0,    });}
# Good: Slurpy parameter for variable argssub log_message($level, @details) {    say "[$level] " . join(' ', @details);}
# Bad: Manual argument unpackingsub connect_db {    my ($host, $port, $timeout) = @_;    $port    //= 5432;    $timeout //= 30;    # ...}

3. Context Sensitivity

Understand scalar vs list context — a core Perl concept.

perl
use v5.36;
my @items = (1, 2, 3, 4, 5);
my @copy  = @items;            # List context: all elementsmy $count = @items;            # Scalar context: count (5)say "Items: " . scalar @items; # Force scalar context

4. Postfix Dereferencing

Use postfix dereference syntax for readability with nested structures.

perl
use v5.36;
my $data = {    users => [        { name => 'Alice', roles => ['admin', 'user'] },        { name => 'Bob',   roles => ['user'] },    ],};
# Good: Postfix dereferencingmy @users = $data->{users}->@*;my @roles = $data->{users}[0]{roles}->@*;my %first = $data->{users}[0]->%*;
# Bad: Circumfix dereferencing (harder to read in chains)my @users = @{ $data->{users} };my @roles = @{ $data->{users}[0]{roles} };

5. The isa Operator (5.32+)

Infix type-check — replaces blessed($o) && $o->isa('X').

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

Error Handling

eval/die Pattern

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 (Reliable Exception Handling)

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

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

Modern OO with Moo

Prefer Moo for lightweight, modern OO. Use Moose only when its metaprotocol is needed.

perl
# Good: Moo classpackage 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;
# Usagemy $user = User->new(    name  => 'Alice',    email => '[email protected]',    roles => ['admin', 'user'],);
# Bad: Blessed hashref (no validation, no accessors)package User;sub new {    my ($class, %args) = @_;    return bless \%args, $class;}sub name { return $_[0]->{name} }1;

Moo Roles

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;

Native class Keyword (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

Regular Expressions

Named Captures and /x Flag

perl
use v5.36;
# Good: Named captures with /x for readabilitymy $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: Positional captures (hard to maintain)if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+\[(\w+)\]\s+(.+)$/) {    say "Time: $1, Level: $2";}

Precompiled Patterns

perl
use v5.36;
# Good: Compile once, use manymy $email_re = qr/^[A-Za-z0-9._%+-]+\@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
sub validate_emails(@emails) {    return grep { $_ =~ $email_re } @emails;}

Data Structures

References and Safe Deep Access

perl
use v5.36;
# Hash and array referencesmy $config = {    database => {        host => 'localhost',        port => 5432,        options => ['utf8', 'sslmode=require'],    },};
# Safe deep access (returns undef if any level missing)my $port = $config->{database}{port};           # 5432my $missing = $config->{cache}{host};           # undef, no error
# Hash slicesmy %subset;@subset{qw(host port)} = @{$config->{database}}{qw(host port)};
# Array slicesmy @first_two = $config->{database}{options}->@[0, 1];
# Multi-variable for loop (experimental in 5.36, stable in 5.40)use feature 'for_list';no warnings 'experimental::for_list';for my ($key, $val) (%$config) {    say "$key => $val";}

File I/O

Three-Argument Open

perl
use v5.36;
# Good: Three-arg open with autodie (core module, eliminates 'or die')use autodie;
sub read_file($path) {    open my $fh, '<:encoding(UTF-8)', $path;    local $/;    my $content = <$fh>;    close $fh;    return $content;}
# Bad: Two-arg open (shell injection risk, see perl-security)open FH, $path;            # NEVER do thisopen FH, "< $path";        # Still bad — user data in mode string

Path::Tiny for File Operations

perl
use v5.36;use Path::Tiny;
my $file = path('config', 'app.json');my $content = $file->slurp_utf8;$file->spew_utf8($new_content);
# Iterate directoryfor my $child (path('src')->children(qr/\.pl$/)) {    say $child->basename;}

Module Organization

Standard Project Layout

text
MyApp/├── lib/│   └── MyApp/│       ├── App.pm           # Main module│       ├── Config.pm        # Configuration│       ├── DB.pm            # Database layer│       └── Util.pm          # Utilities├── bin/│   └── myapp                # Entry-point script├── t/│   ├── 00-load.t            # Compilation tests│   ├── unit/                # Unit tests│   └── integration/         # Integration tests├── cpanfile                 # Dependencies├── Makefile.PL              # Build system└── .perlcriticrc            # Linting config

Exporter Patterns

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;

Tooling

perltidy Configuration (.perltidyrc)

text
-i=4        # 4-space indent-l=100      # 100-char line length-ci=4       # continuation indent-ce         # cuddled else-bar        # opening brace on same line-nolq       # don't outdent long quoted strings

perlcritic Configuration (.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

Dependency Management (cpanfile + carton)

bash
cpanm App::cpanminus Carton   # Install toolscarton install                 # Install deps from cpanfilecarton exec -- perl bin/myapp  # Run with local deps
perl
# cpanfilerequires 'Moo', '>= 2.005';requires 'Path::Tiny';requires 'JSON::MaybeXS';requires 'Try::Tiny';
on test => sub {    requires 'Test2::V0';    requires 'Test::MockModule';};

Quick Reference: Modern Perl Idioms

Legacy PatternModern Replacement
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 hashrefMoo class with types
$1, $2, $3$+{name} (named captures)
eval { }; if ($@)Try::Tiny or native try/catch (5.40+)
BEGIN { require Exporter; }use Exporter 'import';
Manual file opsPath::Tiny
blessed($o) && $o->isa('X')$o isa 'X' (5.32+)
builtin::true / falseuse builtin 'true', 'false'; (5.36+, experimental)

Anti-Patterns

perl
# 1. Two-arg open (security risk)open FH, $filename;                     # NEVER
# 2. Indirect object syntax (ambiguous parsing)my $obj = new Foo(bar => 1);            # Badmy $obj = Foo->new(bar => 1);           # Good
# 3. Excessive reliance on $_map { process($_) } grep { validate($_) } @items;  # Hard to followmy @valid = grep { validate($_) } @items;           # Better: break it upmy @results = map { process($_) } @valid;
# 4. Disabling strict refsno strict 'refs';                        # Almost always wrong${"My::Package::$var"} = $value;         # Use a hash instead
# 5. Global variables as configurationour $TIMEOUT = 30;                       # Bad: mutable globaluse constant TIMEOUT => 30;              # Better: constant# Best: Moo attribute with default
# 6. String eval for module loadingeval "require $module";                  # Bad: code injection riskeval "use $module";                      # Baduse Module::Runtime 'require_module';    # Good: safe module loadingrequire_module($module);

Remember: Modern Perl is clean, readable, and safe. Let use v5.36 handle the boilerplate, use Moo for objects, and prefer CPAN's battle-tested modules over hand-rolled solutions.

來源與署名

來源:affaan-m/ECC位於skills/perl-patterns提交ef648e0

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架