Slide 1

Slide 1 text

令和最新版 Perlコーディングガイド id:anatofuz 2024/10/05 YAPC::Hakodate 2024 1

Slide 2

Slide 2 text

my $self = shift; ● 八雲アナグラ(id:anatofuz) ● (心は)Okinawa.pmの京都在住 ● 株式会社はてな ○ システムプラットフォームチーム ○ Webアプリケーションエンジニア ● PerlとTypeScriptが主戦場 2

Slide 3

Slide 3 text

【PR】 ● 推し(宝灯桃汁)がメンバーの 「3道県をつなぐスタンプラリー」 開催中 ● 各位このあと函館から鶴居村に ● https://houtoumomojiru.info/『3道県をつなぐスタ ンプラリー』をコラボ開催!/ 3

Slide 4

Slide 4 text

hatena.co.jp/recruit 4 4

Slide 5

Slide 5 text

あれから6年 5

Slide 6

Slide 6 text

6 令和(令和6年)最新版 Perlコーディングガイド

Slide 7

Slide 7 text

7 令和最新

Slide 8

Slide 8 text

Perl5.40.0 (最新安定版) 実際にプロダクトで稼働 8

Slide 9

Slide 9 text

9 おことわり ● 本トークでは主にWebアプリケーションや自前 スクリプトなど動作するPerlの環境を決められ る状態でのPerlにフォーカスします ● CPANモジュールを新たに作る上での書き方の 推奨というわけではないです

Slide 10

Slide 10 text

10 トピック ● コーディングガイド ● 特別付録 コーディング以前(環境構築)ガイド

Slide 11

Slide 11 text

11 令和(令和6年)最新版 Perlコーディング

Slide 12

Slide 12 text

12 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン 5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...

Slide 13

Slide 13 text

よいこのお約束 use strict; use warnings; use utf8; 13 現代のPerlでとりあえず書く 3行 いにしえ

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

よいこのお約束 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’;(新組み込み関数開放 )

Slide 16

Slide 16 text

16 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン 5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...

Slide 17

Slide 17 text

伝統のやり方 17 ● evalブロックを使った 素朴なやり方 ● 罠多い ● 初学者からするとむずい ○ 文字列eval ○ ブロックeval eval { die 'foo'; }; if ($@) { print $@; }

Slide 18

Slide 18 text

18 ここから先の令和最新Perl事情 ● Perl本体の開発がここ数年にないほど活発 ○ 言語機能の拡張が非常に増えている ■ Class, bultin ● モジュールで戦い切る、ではなくモジュールが 新機能の検証の道具も担ってきている ○ 検証に成功すると本体に取り込まれる ○ モダンPerlのアプローチとは別アプローチ

Slide 19

Slide 19 text

最近のモジュール事情 ● モジュールが跋扈していた時代からある程度の 統一になりつつある ○ 型システム → Type::Tiny → Data::Checks? ○ 関数定義 → Function::Parameters → signatures ○ クラスビルダ → Object::Pad → builtin class 19

Slide 20

Slide 20 text

かつてのtry-catch 20 ● Try::Tiny ● try-catchっぽいブ ロックはそれぞれただ の関数呼び出しになる ● 意外とハマりどころが 多い use Try::Tiny; try { die "foo"; } catch { warn "caught error: $_"; };

Slide 21

Slide 21 text

平成最後のtry-catch 21 ● Syntax::Keyword::Try ● Try::Tinyよりハマりど ころ少ない ● try-catchがサブルーチ ンではない use Syntax::Keyword::Try; try { die "foo"; } catch($e) { warn "caught error: $e"; }

Slide 22

Slide 22 text

令和最新のtry-catch 22 ● Syntax::Keyword::Try ベースの実装がbuiltinに ● 5.34から実験的機能とし て追加 ○ 5.40から実験的機能でなく なった ● v5.40すると使用可能 use v5.40; try { die "foo"; } catch($e) { warn "caught error: $e"; }

Slide 23

Slide 23 text

23 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン 5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...

Slide 24

Slide 24 text

最近までの罠 ● あるモジュールのコード全文 ○ このコードにはミスがあります ○ どーこだ 24 package Acme::AnaTofuZ; use strict; use warnings; sub hello { print "hello" }

Slide 25

Slide 25 text

最近までの罠 ● あるモジュールのコード全文 ○ このコードにはミスがあります ○ どーこだ 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" }

Slide 26

Slide 26 text

最近までの罠 ● 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;

Slide 27

Slide 27 text

令和最新版Perl ● use v5.38以上かuse feature qw(module_true)で真の値返さなくても動作 するように 27 package Acme::AnaTofuZ; use v5.40; sub hello { print "hello" }

Slide 28

Slide 28 text

28 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン 5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...

Slide 29

Slide 29 text

レキシカルサブルーチン ● 宣言されたブロックの中で有効なサブルーチンを定 義できる ● 5.26から正式な機能 ● ちゃんとしたプライベート メッドとしても使える 29 use feature 'say'; sub foo { my sub bar { say 'bar'; } bar(); } # 未定義の関数呼び出しによる例外が発生する。 bar();

Slide 30

Slide 30 text

例えばテストツールとして 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; };

Slide 31

Slide 31 text

31 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン 5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...

Slide 32

Slide 32 text

Perlの関数といえば ● Perlの関数の引数は全て配列@_に設定される ● イディオム的な処理で色々工夫 ○ ハッシュで受け取ってキーワード引数 32 sub func1 { my ($arg1, $arg2, %args) = @_; my $opt1 = $args{opt1}; my $opt2 = $args{opt2}; } func1('hoge', 'fuga', opt1 => 1, opt2 => 2);

Slide 33

Slide 33 text

@_むずい ● @_はPerl初心者には扱いが難しい ● 引数のチェック機能がほぼない ○ 数が足りないとか名前が違うとかは自分でチェック必須 33 sub func1 { my ($arg1, $arg2, %args) = @_; my $opt1 = $args{opt1}; my $opt2 = $args{opt2}; } func1('hoge', 'fuga', opt1 => 1, opt2 => 2);

Slide 34

Slide 34 text

引数の型表明 ● @_使わずに引数を表現したい ● CPANモジュールでいい感じのものがある ○ Smart::Args::TypeTiny ■ はてなでの主流 ○ Function::Parameters ● Webアプリケーションではこのあたりを使って 引数の型チェック/名前付き呼び出しをしたい 34

Slide 35

Slide 35 text

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], ; ... }

Slide 36

Slide 36 text

Function::Parameters ● 同じような機能のモジュールがある ○ 型検査の無効化の方法などの差異はある ● 海外ではこちらがよく使われがち 36 use Function::Parameters; use Types::Standard -types; fun foo(Str :$label, ArrayRef[Int] :$counts) { return $label; }

Slide 37

Slide 37 text

令和最新版関数の引数 ● @_から型と組み合わせて定義する方向に ○ Type::Tinyとの組み合わせ力💪 ○ Perlコードによって型チェックと引数処理が行われるの で、実行時チェックが基本 ■ 本番環境では速度のためにチェックを切るなども可能 ● Function::Parametersの成果がPerl本体に取 り込まれつつある 37

Slide 38

Slide 38 text

サブルーチンシグネチャ機能 ● 5.36から実験的扱いでなくなったコア機能 ● 引数が他の言語の関数定義っぽく書ける ○ 関数の個数の保証 ○ 足りなかった引数のデフォルト値のセット ● 型のチェックはできない ○ それくらいの粒度で良ければ便利 38 sub foo ($first_name, $surname, $nickname = $first_name) { print "$first_name $surname is known as \"$nickname\""; }

Slide 39

Slide 39 text

未来の関数シグネチャ ● PPC 0024で Named Parameter が議論されている ○ 将来的に入るかも 39 sub func ( :$abc, :$xyz, ... ) { ... } func( abc => 123, %args, xyz => 789, );

Slide 40

Slide 40 text

40 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン 5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...

Slide 41

Slide 41 text

関数の返り値 ● 関数の返り値を何かしらの方法で可視化したい ○ テストコードを全部みる! ○ コメントで書く 41

Slide 42

Slide 42 text

かつての俺達の関数の返り値 ● 温かみのあるコメントで表現 ● 機械的なチェックが無いので表記ゆれがち ○ 実装の修正にコメントの追従が漏れるケースも ● 人間がわかる以上の恩恵がない 42 # id の配列からユーザを取得する # returns: HashRef[id => Model::UserAccount] sub find_by_ids { args my $class => 'ClassName', my $db => 'AnaTofuZ::DBFactory', my $ids => 'ArrayRef[Int]';

Slide 43

Slide 43 text

関数の返り値のコメント ● テストなどでも活用したい ● アノテーション形式のCPANモジュール ○ Function::Return ○ Return::Type ● コメント文化からはギャップがある ○ 別のアプローチでの返り値チェック機能を実装 43 use Function::Return; use Types::Standard -types; sub foo :Return(Int) { 123 } sub bar :Return(Int) { 3.14 }

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

返り値の表明 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]';

Slide 46

Slide 46 text

返り値の表明 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]';

Slide 47

Slide 47 text

返り値の表明 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 関数名 => 返り値の型」 返り値を返す際に型チェック

Slide 48

Slide 48 text

resを使った返り値の表明 48 find_by_ids 通常実行 メインルーチン res経由 find_by_ids res (Type::Tinyによる型 チェック)

Slide 49

Slide 49 text

resを使った返り値の表明 49 find_by_ids 通常実行 メインルーチン res経由 find_by_ids res (Type::Tinyによる型 チェック) メタプログラミングで resの対象の関数をラップ する

Slide 50

Slide 50 text

返り値チェック 50 ● Type::Tinyの型を使って返り値チェック ○ 自動的に書式が統一化される ● 従来のコメントでの型定義からのスムーズな移行 ● Perlのメタプログラミング活用 ○ 環境変数でテスト環境が指定されていた場合のみ チェックが有効化 ○ 本番環境ではチェックフローがスキップされる

Slide 51

Slide 51 text

ビジネスロジックの例 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', ; ある程度複雑な型構造 (入れ子のハッシュ形式)

Slide 52

Slide 52 text

ビジネスロジックの例 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での 引数の型宣言

Slide 53

Slide 53 text

ビジネスロジックの例 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', ;

Slide 54

Slide 54 text

54 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン 5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...

Slide 55

Slide 55 text

型によるパターンマッチ 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 漫画家 }

Slide 56

Slide 56 text

56 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン 5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...

Slide 57

Slide 57 text

JSONエンコード 57 ● Perlの変数は数値と文字列の区別がコンテキス ト依存 ● JSONエンコード時になにかしらの方法で任意 の型に強制する必要がある

Slide 58

Slide 58 text

平成の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"}

Slide 59

Slide 59 text

平成の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"}

Slide 60

Slide 60 text

平成の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"}

Slide 61

Slide 61 text

令和最新の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);

Slide 62

Slide 62 text

令和最新の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);

Slide 63

Slide 63 text

63 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン 5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...

Slide 64

Slide 64 text

useお約束セットができてきた 64 ● Type::Tinyは便利だけど use必須 ○ あらゆる場所で useしまくらないといけないの大変 ● use v5.40も便利だけれど ○ アプリの全ファイルでバージョン指定するの大変 ■ Perlのバージョン変えたら一箇所だけ変更する PRを送って解決したい

Slide 65

Slide 65 text

プラグマ ● プラグマは、strict や warnings のように、コ ンパイル時や実行時の Perl の 振る舞いにある 種の影響を与えるモジュールです ○ https://perldoc.jp/docs/perl/5.40.0/perlpragma.pod ● 組み込みプラグマ以外に自作も可能 65

Slide 66

Slide 66 text

自作プラグマ ● import / unimportという名前で関数を定義す るとuse時及びuseの効力を失うときに発火す る関数を書ける ● import時に呼び出し元のpackageでuseしたこ とと等価なコードを書くとオレオレプラグマが できる 66

Slide 67

Slide 67 text

use ful; 67 ● チームに名前を公募して 決定 ● 以下と等価 ○ use v5.40; ○ use Types::Standard -types; ○ use utf8; ● 類似品にModern::Perl モジュール package Perl::Example; use ful; use Carp qw(croak); …

Slide 68

Slide 68 text

use ful;でできること ● try-catch ● 型を使った表現 ○ 引数 ○ 返り値 ● 組み込みclass, 組み込みtrue,false … ○ useするだけで令和最新版のPerlへの道が開かれる 68

Slide 69

Slide 69 text

69 令和最新版コーディングガイド 1. よいこのお約束 2. try-catch 3. .pmのおまじない 4. レキシカルサブルーチン 5. 引数の型 6. 返り値の型 7. パターンマッチ 8. JSONエンコード 9. 令和最新版use 10. そして伝説へ...

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

よいこのお約束 use strict; use warnings; use utf8; 71 use v5.26; use warnings; use utf8; いにしえ 平成最後 use v5.40; use utf8; 令和最新 use ful; # これが真・令和最新版

Slide 72

Slide 72 text

まとめ ● 令和最新版Perl 5.40での書き方をみた ○ 思ったより現代化していると思っていただければ ○ TMTOWTDIなので別のやり方も全然アリだと思います ○ 今もPerlはuseful ● @kfly8++ ○ 同じチームで開発していたときに色々やってました 72

Slide 73

Slide 73 text

73 令和(令和6年)最新版 Perlコーディング以前 (特別付録)

Slide 74

Slide 74 text

74 コーディング以前 ● 環境構築系 ○ 言語バージョン管理 ○ パッケージ管理 ● 開発支援 ○ LSP ○ Linter ○ Formatter

Slide 75

Slide 75 text

75 コーディング以前 ● 手元で複数バージョン切り替えたい ○ perlblew ○ plenv ○ mise ● docker imageはdocker-perlリポジトリで管理 ○ cpanm/cpmがデフォルトでバンドルされてる ○ mod_perlはmotemenさんが趣味で作ってる

Slide 76

Slide 76 text

76 パッケージ管理 ● Carmel ○ cpanfileを使ったバージョン管理 ○ Cartonの後継 ■ キャッシュシステムもあり爆速 ● cpm ○ 並列でモジュールインストールをするので高速 ○ Carmelと同様にcpanfileにも対応 ○ cpmでcpmをinstallすることも可能

Slide 77

Slide 77 text

77 PerlNavigator ● かなり出来がいいLSP ● 主戦場はVSCodeだが他のエディタでも使える ● コードジャンプやオブジェクトの推論も ○ プロジェクトのCPANモジュールをエディタが動いている 環境にインストールする必要がある ● linter/formatterとの連携もある

Slide 78

Slide 78 text

78 パッケージ管理 ● Perl::Critic ○ いわゆるLinter ○ 自分でPolicy(ルール)を記述することも可能 ● Perl::Tidy ○ いわゆるFormatter ● App::perlimports ○ golangのgoimports的な開発支援ツール

Slide 79

Slide 79 text

いい感じに使いたいあなたに 79 ● pre-commit-perl ○ git commitする前に自動で このあたりのコマンドを 実行してくれるくん