Upgrade to Pro — share decks privately, control downloads, hide ads and more …

令和最新版 Perlコーディングガイド

AnaTofuZ
October 05, 2024

令和最新版 Perlコーディングガイド

YAPC::Hakodate 2024でのトークです
https://yapcjapan.org/2024hakodate/

AnaTofuZ

October 05, 2024
Tweet

More Decks by AnaTofuZ

Other Decks in Technology

Transcript

  1. my $self = shift; • 八雲アナグラ(id:anatofuz) • (心は)Okinawa.pmの京都在住 • 株式会社はてな

    ◦ システムプラットフォームチーム ◦ Webアプリケーションエンジニア • PerlとTypeScriptが主戦場 2
  2. 12 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン

    5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...
  3. よいこのお約束 use strict; use warnings; use utf8; 14 use v5.26;

    use warnings; use utf8; いにしえ 平成最後 use strict; use feature ‘:5.26’;(新機能開放 )
  4. よいこのお約束 use strict; use warnings; use utf8; 15 use v5.26;

    use warnings; use utf8; いにしえ 平成最後 use v5.40; use utf8; 令和最新 use warnings;(5.35以降) use builtin ‘:5.40’;(新組み込み関数開放 )
  5. 16 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン

    5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...
  6. 18 ここから先の令和最新Perl事情 • Perl本体の開発がここ数年にないほど活発 ◦ 言語機能の拡張が非常に増えている ▪ Class, bultin •

    モジュールで戦い切る、ではなくモジュールが 新機能の検証の道具も担ってきている ◦ 検証に成功すると本体に取り込まれる ◦ モダンPerlのアプローチとは別アプローチ
  7. 23 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン

    5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...
  8. 最近までの罠 • あるモジュールのコード全文 ◦ このコードにはミスがあります ◦ どーこだ 25 Acme/AnaTofuZ.pm did

    not return a true value at sample.pl line 3. BEGIN failed--compilation aborted at sample.pl line 3. package Acme::AnaTofuZ; use strict; use warnings; sub hello { print "hello" }
  9. 最近までの罠 • Perlのモジュールは最後に真の値を返さないと ならない!!! 26 Acme/AnaTofuZ.pm did not return a

    true value at sample.pl line 3. BEGIN failed--compilation aborted at sample.pl line 3. package Acme::AnaTofuZ; use strict; use warnings; sub hello { print "hello" } 1;
  10. 28 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン

    5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...
  11. 例えばテストツールとして 30 • 従来(サブルーチンリ ファレンス)を利用 subtest 'add' => sub {

    my sub subject($a, $b) { add($a, $b) } is subject(1, 2), 3; is subject(4, 5), 6; }; • 最新版 subtest 'add' => sub { my $subject = sub ($a, $b) { add($a, $b) } is $subject->(1, 2), 3; is $subject->(4, 5), 6; };
  12. 31 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン

    5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...
  13. 引数の型表明 • @_使わずに引数を表現したい • CPANモジュールでいい感じのものがある ◦ Smart::Args::TypeTiny ▪ はてなでの主流 ◦

    Function::Parameters • Webアプリケーションではこのあたりを使って 引数の型チェック/名前付き呼び出しをしたい 34
  14. Smart::Args::TypeTiny • `引数名 => 型名`の組で関数の引数を定義できる ◦ 型は主にType::Tinyで定義される 35 use Smart::Args::TypeTiny

    qw(args); use Types::Standard -types; sub foo { args $class, $label => Str, $counts => ArrayRef[Int], ; ... }
  15. サブルーチンシグネチャ機能 • 5.36から実験的扱いでなくなったコア機能 • 引数が他の言語の関数定義っぽく書ける ◦ 関数の個数の保証 ◦ 足りなかった引数のデフォルト値のセット •

    型のチェックはできない ◦ それくらいの粒度で良ければ便利 38 sub foo ($first_name, $surname, $nickname = $first_name) { print "$first_name $surname is known as \"$nickname\""; }
  16. 未来の関数シグネチャ • PPC 0024で Named Parameter が議論されている ◦ 将来的に入るかも 39

    sub func ( :$abc, :$xyz, ... ) { ... } func( abc => 123, %args, xyz => 789, );
  17. 40 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン

    5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...
  18. かつての俺達の関数の返り値 • 温かみのあるコメントで表現 • 機械的なチェックが無いので表記ゆれがち ◦ 実装の修正にコメントの追従が漏れるケースも • 人間がわかる以上の恩恵がない 42

    # id の配列からユーザを取得する # returns: HashRef[id => Model::UserAccount] sub find_by_ids { args my $class => 'ClassName', my $db => 'AnaTofuZ::DBFactory', my $ids => 'ArrayRef[Int]';
  19. 関数の返り値のコメント • テストなどでも活用したい • アノテーション形式のCPANモジュール ◦ Function::Return ◦ Return::Type •

    コメント文化からはギャップがある ◦ 別のアプローチでの返り値チェック機能を実装 43 use Function::Return; use Types::Standard -types; sub foo :Return(Int) { 123 } sub bar :Return(Int) { 3.14 }
  20. 返り値の表明 44 # id の配列からユーザを取得する # returns: HashRef[id => Model::UserAccount]

    sub find_by_ids { args my $class => 'ClassName', my $db => 'AnaTofuZ::DBFactory', my $ids => 'ArrayRef[Int]';
  21. 返り値の表明 45 use Types::Standard; # id の配列からユーザを取得する # returns: HashRef[id

    => Model::UserAccount] sub find_by_ids { args my $class => 'ClassName', my $db => 'AnaTofuZ::DBFactory', my $ids => 'ArrayRef[Int]';
  22. 返り値の表明 46 use Types::Standard; # id の配列からユーザを取得する res find_by_ids =>

    Map[PositiveInt,InstanceOf['Model::UserAccount']]; sub find_by_ids { args my $class => 'ClassName', my $db => 'AnaTofuZ::DBFactory', my $ids => 'ArrayRef[Int]';
  23. 返り値の表明 47 use Types::Standard qw(Int); # id の配列からユーザを取得する res find_by_ids

    => Map[PositiveInt,InstanceOf['Model::UserAccount']]; sub find_by_ids { args my $class => 'ClassName', my $db => 'AnaTofuZ::DBFactory', my $ids => 'ArrayRef[Int]'; 「res 関数名 => 返り値の型」 返り値を返す際に型チェック
  24. 返り値チェック 50 • Type::Tinyの型を使って返り値チェック ◦ 自動的に書式が統一化される • 従来のコメントでの型定義からのスムーズな移行 • Perlのメタプログラミング活用

    ◦ 環境変数でテスト環境が指定されていた場合のみ チェックが有効化 ◦ 本番環境ではチェックフローがスキップされる
  25. ビジネスロジックの例 51 use constant userIdToScores => Map [ PositiveInt, PositiveOrZeroInt,];

    res calculate_ranking => Dict [ users => ArrayRef [ InstanceOf ['Game::Model::User'] ], user_id_to_scores => userIdToScores, ]; sub calculate_ranking { args my $class => 'ClassName', my $db => 'Game::DBFactory', my $datetime => 'Game::DateTime', ; ある程度複雑な型構造 (入れ子のハッシュ形式)
  26. ビジネスロジックの例 52 use constant userIdToScores => Map [ PositiveInt, PositiveOrZeroInt,];

    res calculate_ranking => Dict [ users => ArrayRef [ InstanceOf ['Game::Model::User'] ], user_id_to_scores => userIdToScores, ]; sub calculate_ranking { args my $class => 'ClassName', my $db => 'Game::DBFactory', my $datetime => 'Game::DateTime', ; Smart::Args::TypeTinyでの 引数の型宣言
  27. ビジネスロジックの例 53 use constant userIdToScores => Map [ PositiveInt, PositiveOrZeroInt,];

    res calculate_ranking => Dict [ users => ArrayRef [ InstanceOf ['Game::Model::User'] ], user_id_to_scores => userIdToScores, ]; sub calculate_ranking { args my $class => 'ClassName', my $db => 'Game::DBFactory', my $datetime => 'Game::DateTime', ;
  28. 54 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン

    5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...
  29. 型によるパターンマッチ 55 • 型によるパターンマッチ ◦ 直和型も作成可能 use constant Movie =>

    Dict[]; use constant Comic => Str & sub { $_ eq 'comic' }; use constant Media => Movie | Comic; sub to_label { my $self = shift; state $kind_to_label = compile_match_on_type( Movie, sub { '映画化' }, Comic, sub { '漫画化' }, ); $kind_to_label->($self->kind); #映画化 or 漫画家 }
  30. 56 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン

    5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...
  31. 平成のJSONエンコード 58 • JSON::XSとJSON::Typesの合せ技 ◦ JSON::XSでは最後に変数が評価されたコンテキス トでJSONの型を決定する use JSON::XS; use

    JSON::Types (); my $json_str = encode_json({ id => JSON::Types::number($id), is_boolean => JSON::Types::bool(true), id_str => JSON::Types::string($id), }); # {"is_boolean":true,"id":0,"id_str":"0"}
  32. 平成のJSONエンコード 59 • JSON::XSとJSON::Typesの合せ技 ◦ JSON::Typesで強制したい型のコンテキストで変 数を評価する use JSON::XS; use

    JSON::Types (); my $json_str = encode_json({ id => JSON::Types::number($id), is_boolean => JSON::Types::bool(true), id_str => JSON::Types::string($id), }); # {"is_boolean":true,"id":0,"id_str":"0"}
  33. 平成のJSONエンコード 60 • encode_jsonの直前にJSON::Typesを呼ぶ ◦ JSON::Typesとencode_jsonの間に変数に触れる と予期せぬ状態になりがち use JSON::XS; use

    JSON::Types (); my $json_str = encode_json({ id => JSON::Types::number($id), is_boolean => JSON::Types::bool(true), id_str => JSON::Types::string($id), }); # {"is_boolean":true,"id":0,"id_str":"0"}
  34. 令和最新のJSONエンコード 61 • Cpanel::JSON::XSを採用 ◦ encode_jsonの第二引数に型情報を渡せる ▪ Type::TinyからCpanel::JSONの型変換 use Cpanel::JSON::XS;

    use constant JsonType => Dict[ id => PositiveInt, is_boolean => Bool, is_str => Str, ]; my $type = type_to_cpanel_type(JsonType); say encode_json({ id => '42', is_boolean => false, is_str => 42 }, $type);
  35. 令和最新のJSONエンコード 62 • JSONライブラリ側で型変換してくれるので安心感 ++ ◦ 型情報もType::Tinyのものを使い回せる ▪ 見通しのよさ++ use

    Cpanel::JSON::XS; use constant JsonType => Dict[ id => PositiveInt, is_boolean => Bool, is_str => Str, ]; my $type = type_to_cpanel_type(JsonType); say encode_json({ id => '42', is_boolean => false, is_str => 42 }, $type);
  36. 63 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン

    5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...
  37. useお約束セットができてきた 64 • Type::Tinyは便利だけど use必須 ◦ あらゆる場所で useしまくらないといけないの大変 • use

    v5.40も便利だけれど ◦ アプリの全ファイルでバージョン指定するの大変 ▪ Perlのバージョン変えたら一箇所だけ変更する PRを送って解決したい
  38. プラグマ • プラグマは、strict や warnings のように、コ ンパイル時や実行時の Perl の 振る舞いにある

    種の影響を与えるモジュールです ◦ https://perldoc.jp/docs/perl/5.40.0/perlpragma.pod • 組み込みプラグマ以外に自作も可能 65
  39. use ful; 67 • チームに名前を公募して 決定 • 以下と等価 ◦ use

    v5.40; ◦ use Types::Standard -types; ◦ use utf8; • 類似品にModern::Perl モジュール package Perl::Example; use ful; use Carp qw(croak); …
  40. use ful;でできること • try-catch • 型を使った表現 ◦ 引数 ◦ 返り値

    • 組み込みclass, 組み込みtrue,false … ◦ useするだけで令和最新版のPerlへの道が開かれる 68
  41. 69 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン

    5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...
  42. よいこのお約束 use strict; use warnings; use utf8; 70 use v5.26;

    use warnings; use utf8; いにしえ 平成最後 use v5.40; use utf8; 令和最新
  43. よいこのお約束 use strict; use warnings; use utf8; 71 use v5.26;

    use warnings; use utf8; いにしえ 平成最後 use v5.40; use utf8; 令和最新 use ful; # これが真・令和最新版
  44. 75 コーディング以前 • 手元で複数バージョン切り替えたい ◦ perlblew ◦ plenv ◦ mise

    • docker imageはdocker-perlリポジトリで管理 ◦ cpanm/cpmがデフォルトでバンドルされてる ◦ mod_perlはmotemenさんが趣味で作ってる
  45. 76 パッケージ管理 • Carmel ◦ cpanfileを使ったバージョン管理 ◦ Cartonの後継 ▪ キャッシュシステムもあり爆速

    • cpm ◦ 並列でモジュールインストールをするので高速 ◦ Carmelと同様にcpanfileにも対応 ◦ cpmでcpmをinstallすることも可能
  46. 78 パッケージ管理 • Perl::Critic ◦ いわゆるLinter ◦ 自分でPolicy(ルール)を記述することも可能 • Perl::Tidy

    ◦ いわゆるFormatter • App::perlimports ◦ golangのgoimports的な開発支援ツール