首页/后端开发/perl-patterns
P

perl-patterns

by @affaan-mv1.0.0
0.0(0)

提供现代Perl开发模式和最佳实践,遵循Perl 5.36+规范,提升代码质量和开发效率。

Perl DevelopmentCoding PatternsBackend EngineeringBest PracticesGitHub
安装方式
npx skills add affaan-m/everything-claude-code --skill perl-patterns
compare_arrows

Before / After 效果对比

1
使用前

Perl代码风格不统一,可读性差,难以维护和协作。

使用后

遵循现代Perl开发模式,代码规范,提升可读性和维护性。

description SKILL.md

perl-patterns

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. # Good: Modern preamble use v5.36; sub greet($name) { say "Hello, $name!"; } # Bad: Legacy boilerplate use 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. use v5.36; # Good: Signatures with defaults sub 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 args sub log_message($level, @details) { say "[$level] " . join(' ', @details); } # Bad: Manual argument unpacking sub connect_db { my ($host, $port, $timeout) = @; $port //= 5432; $timeout //= 30; # ... } 3. Context Sensitivity Understand scalar vs list context — a core Perl concept. use v5.36; my @items = (1, 2, 3, 4, 5); my @copy = @items; # List context: all elements my $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. use v5.36; my $data = { users => [ { name => 'Alice', roles => ['admin', 'user'] }, { name => 'Bob', roles => ['user'] }, ], }; # Good: Postfix dereferencing my @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'). use v5.36; if ($obj isa 'My::Class') { $obj->do_something } Error Handling eval/die Pattern 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) 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+) 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. # Good: Moo class 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; # Usage my $user = User->new( name => 'Alice', email => 'alice@example.com', 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 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) use v5.38; use feature 'class'; no warnings 'experimental::class'; class Point { field $x :param; field $y :param; method magnitude() { sqrt($x2 + $y2) } } my $p = Point->new(x => 3, y => 4); say $p->magnitude; # 5 Regular Expressions Named Captures and /x Flag use v5.36; # Good: Named captures with /x for readability my $log_re = qr{ ^ (? \d{4}-\d{2}-\d{2} \s \d{2}:\d{2}:\d{2} ) \s+ [ (? \w+ ) ] \s+ (? .+ ) $ }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 use v5.36; # Good: Compile once, use many 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; } Data Structures References and Safe Deep Access use v5.36; # Hash and array references my $config = { database => { host => 'localhost', port => 5432, options => ['utf8', 'sslmode=require'], }, }; # Safe deep access (returns undef if any level missing) my $port = $config->{database}{port}; # 5432 my $missing = $config->{cache}{host}; # undef, no error # Hash slices my %subset; @subset{qw(host port)} = @{$config->{database}}{qw(host port)}; # Array slices my @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 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 this open FH, "< $path"; # Still bad — user data in mode string Path::Tiny for File Operations use v5.36; use Path::Tiny; my $file = path('config', 'app.json'); my $content = $file->slurp_utf8; $file->spew_utf8($new_content); # Iterate directory for my $child (path('src')->children(qr/.pl$/)) { say $child->basename; } Module Organization Standard Project Layout 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 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) -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) 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 Dependency Management (cpanfile + carton) cpanm App::cpanminus Carton # Install tools carton install # Install deps from cpanfile carton exec -- perl bin/myapp # Run with local deps # cpanfile requires '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 Pattern Modern 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 hashref Moo 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 ops Path::Tiny blessed($o) && $o->isa('X') $o isa 'X' (5.32+) builtin::true / false use builtin 'true', 'false'; (5.36+, experimental) Anti-Patterns # 1. Two-arg open (security risk) open FH, $filename; # NEVER # 2. Indirect object syntax (ambiguous parsing) my $obj = new Foo(bar => 1); # Bad my $obj = Foo->new(bar => 1); # Good # 3. Excessive reliance on $ map { process($) } grep { validate($) } @items; # Hard to follow my @valid = grep { validate($) } @items; # Better: break it up my @results = map { process($) } @valid; # 4. Disabling strict refs no strict 'refs'; # Almost always wrong ${"My::Package::$var"} = $value; # Use a hash instead # 5. Global variables as configuration our $TIMEOUT = 30; # Bad: mutable global use constant TIMEOUT => 30; # Better: constant # Best: Moo attribute with default # 6. String eval for module loading eval "require $module"; # Bad: code injection risk eval "use $module"; # Bad use Module::Runtime 'require_module'; # Good: safe module loading require_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.Weekly Installs213Repositoryaffaan-m/everyt…ude-codeGitHub Stars83.1KFirst Seen7 days agoSecurity AuditsGen Agent Trust HubPassSocketPassSnykPassInstalled oncodex201cursor168gemini-cli167amp167cline167github-copilot167

forum用户评价 (0)

发表评价

效果
易用性
文档
兼容性

暂无评价,来写第一条吧

统计数据

安装量0
评分0.0 / 5.0
版本1.0.0
更新日期2026年3月18日
对比案例1 组

用户评分

0.0(0)
5
0%
4
0%
3
0%
2
0%
1
0%

为此 Skill 评分

0.0

兼容平台

🔧Claude Code

时间线

创建2026年3月18日
最后更新2026年3月18日