Slide 1

Slide 1 text

prototype大全 YAPC::Kyoto 2023 @utgwkk (うたがわきき)

Slide 2

Slide 2 text

自己紹介 @utgwkk (うたがわきき) 株式会社はてな Webアプリケーションエンジニア in 京都 最近は主にGoを書いて暮らしています

Slide 3

Slide 3 text

突然ですがこちらのコードをご覧ください (1) push @arr, "new value"; map { $_ + 2 } @arr; try { everything() } catch { up() };

Slide 4

Slide 4 text

突然ですがこちらのコードをご覧ください (2) push @arr, "new value"; # なんでこれでpushできるの? map { $_ + 2 } @arr; # { } の中は何? try { everything() } catch { up() }; # try-catchだ!!1 →prototypeが関係している

Slide 5

Slide 5 text

今日の目標 (1) push @arr, "new value"; map { $_ + 2 } @arr; try { everything() } catch { up() }; これらの裏で起こっていることからprototypeを読み解く

Slide 6

Slide 6 text

今日の目標 (2) ↓のサブルーチン定義 (prototype) の意味が分かるようになる sub foo ($) { } sub bar ($;$@) { } sub mymap (&@) { } sub zip (\@\@) { } sub foo () { 1 }

Slide 7

Slide 7 text

try-catchから始めるprototype入門

Slide 8

Slide 8 text

prototypeとは とても限られた形のコンパイル時引数チェック サブルーチンの引数の解釈方法を制御できる パーサーの挙動が変わる!! (表現力が上がる) メソッドにはprototypeを指定できない

Slide 9

Slide 9 text

prototypeの書き方 (1) sub name (prototype) { ... } サブルーチン定義の後ろに括弧で書く sub name :prototype(prototype) { ... } attributeを使うこともできる (subroutine signaturesと衝突しない)

Slide 10

Slide 10 text

prototypeの書き方 (2) sub name (prototype) { ... } ← この発表ではこっちを使う サブルーチン定義の後ろに括弧で書く sub name :prototype(prototype) { ... } attributeを使うこともできる (subroutine signaturesと衝突しない)

Slide 11

Slide 11 text

prototypeに出てくる記号 シジル (変数につける記号) に対応すると考えるとよさそう $はscalar, @はlist, %はhash, &はサブルーチン, *はglob \を付けるとリファレンスになる \[] で囲むといずれか1つのリファレンス (バックスラッシュが必要) ; 以降は省略可能

Slide 12

Slide 12 text

prototypeの例 sub foo ($) { } sub bar ($;$@) { } sub mymap (&@) { } sub zip (\@\@) { } sub foo () { 1 }

Slide 13

Slide 13 text

prototypeの読み方 (再掲) sub foo ($) { } # 括弧の中に記号を書く sub bar ($;$@) { } # ; 以降は省略可能 sub mymap (&@) { } # &はサブルーチン、@はリスト sub zip (\@\@) { } # リストをリファレンスとして受ける sub foo () { 1 } # 定数をreturnなしで返する

Slide 14

Slide 14 text

prototypeに書けるもの だいたいなんでも書けてすごい だいたいなんでも書けるけどwarningされる sub f (hello!!1) { } # prototypeは hello!!1 sub g (こんにちは) { } # prototypeは こんにちは なんでだいたいなんでも書けるのかは不明 (知ってたら教えてください)

Slide 15

Slide 15 text

prototypeの例 (Try::Tiny) try-catch構文をprototypeで再現 https://metacpan.org/pod/Try::Tiny try { } catch { } finally { }; 最近のPerlにはtry-catch構文が組み込まれている

Slide 16

Slide 16 text

Try::Tinyのコードを読んでいく (1) sub try(&;@) try-catch構文のスタート コードブロック (無名サブルーチン) を受け取る 省略せずに書くと try(sub { }, ...) 例外が送出されたらcatch/finallyブロックの処理が実行される

Slide 17

Slide 17 text

Try::Tinyのコードを読んでいく (2) sub catch(&;@) 例外を捕捉する ブロック内では $_ 変数に例外の内容が入っている 省略せずに書くと catch(sub { }, ...) Try::Tiny::Catch packageでblessしているけど実体は無名サブルーチン

Slide 18

Slide 18 text

Try::Tinyのコードを読んでいく (3) sub finally(&;@) try-catchの最後に必ず呼ばれる finallyブロックの処理はTry::Tiny::ScopeGuard packageでblessされる

Slide 19

Slide 19 text

Try::Tinyのコードを読んでいく (まとめ) try { } catch { } finally { }; try(sub { }, catch(sub { }, finally(sub { }))); 単なるサブルーチン呼び出し式なので、ブロックの後ろに ; が必要 ブロックではなく無名サブルーチンなのでreturnしても関数全体を抜けない (罠)

Slide 20

Slide 20 text

ちなみに別のtry-catchは? Syntax::Keyword::Try syntax pluginという仕組みでtry-catch構文を再現 (セミコロン不要) 詳細は以下の記事など Syntax::Keyword::TryとPerlのキーワードプラグイン (その1) - Masteries 最近のPerlにはtry-catch構文が組み込まれている (再掲)

Slide 21

Slide 21 text

prototypeについて網羅的に知るには (1) perldoc perlsubにまとまっている 今回解説しきれていない項目もあります https://perldoc.perl.org/perlsub#Prototypes https://perldoc.jp/docs/perl/5.36.0/perlsub.pod#Prototypes

Slide 22

Slide 22 text

prototypeについて網羅的に知るには (2) prototypeを使ってtry-catch構文を再現する例がperldoc perlsubに書いてある!!

Slide 23

Slide 23 text

こんなにあるぞ!! prototype

Slide 24

Slide 24 text

こんなにあるぞ!! prototype prototypeによって得られている効能についてご紹介します

Slide 25

Slide 25 text

リストにpushするには push @arr, @elems; sub mypush(\@@) 第1引数はリストのリファレンスを受け取る 第2引数以降はリストとして解釈される 第1引数のリストに破壊的変更を加えることができる

Slide 26

Slide 26 text

mapの正体 map { $_ + 2 } @arr; ブロックだと思っているのは無名サブルーチン ブロック内でreturnしてもサブルーチン全体から抜けるわけではない 紛らわしいのでブロックに渡すサブルーチンを用意するのがよさそう

Slide 27

Slide 27 text

定数展開 sub a1 () { "aaa" } say a1 . "bbb"; コンパイル時に say "aaabbb" に展開される use constantしたときと似たような効能が得られる sub a2 { "aaa" } だと展開されない https://perldoc.perl.org/perlsub#Constant-Functions

Slide 28

Slide 28 text

身近に潜むprototype採用事例 (Test2::V0) is $obj, object { prop isa => 'Foo'; call meth => 'blah'; }; $obj の性質を宣言的にテストできる ちなみにTest::Deepだと↓のように書ける cmp_deeply $obj, isa('Foo') & methods(meth => 'blah);

Slide 29

Slide 29 text

身近に潜むprototype採用事例 (Module::Find) usesub Foo::Plugin; indirectだと思って読むと Foo::Plugin->usesub になるのでは? sub usesub(*) { … } barewordを受け取ってパッケージ名だと解釈できる

Slide 30

Slide 30 text

身近に潜むprototype採用事例 (Scope::Guard) my $g = guard { }; 変数 $g のスコープが終わったらブロックに渡した処理が呼ばれる これも無名サブルーチン スコープが終わったらDESTROYメソッドが呼ばれる 最近のPerlだとdeferがある (experimental)

Slide 31

Slide 31 text

身近に潜むprototype採用事例 (List::MoreUtils) (1) zip6 @xs, @ys, @zs; 6はPerl6 (現Raku) の6 別名zip_unflatten [$xs[0], $ys[0], $zs[0]], [$xs[1], $ys[1], $zs[1]], ... のリストを返す

Slide 32

Slide 32 text

身近に潜むprototype採用事例 (List::MoreUtils) (2) ところで say @xs, @ys, @zs ってやったらリストが平坦になるのでは? zip6 @xs, @ys, @zs では各リストはリファレンスとして渡されている リファレンスなら平坦になることはない % perl -E '@xs = (1, 2); @ys = (3, 4); @zs = (5, 6); say @xs, @ys, @zs' 123456

Slide 33

Slide 33 text

身近に潜むprototype採用事例 (List::MoreUtils) (3) sub zip6 (\@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\ @\@\@\@\@) \@ が32個!! 一番好きなprototypeです リストを33個渡すとコンパイルエラーになる (無限に渡せない) https://metacpan.org/release/REHSACK/List-MoreUtils-0.430/source/lib/List/More Utils/PP.pm#L520

Slide 34

Slide 34 text

身近に潜むprototype採用事例 (List::MoreUtils) (4) 実際にリストを33個渡してみましょう…… (ここでデモ)

Slide 35

Slide 35 text

身近に潜むprototype採用事例 (List::MoreUtils) (5) % carmel exec perl 02-list-moreutils-zip6.pl Too many arguments for List::MoreUtils::XS::zip6 at 02-list-moreutils-zip6.pl line 16, near ")" Execution of 02-list-moreutils-zip6.pl aborted due to compilation errors.

Slide 36

Slide 36 text

身近に潜むprototype採用事例 (ほかいろいろ) きっと無限にprototype採用事例があるはず!! 心当たりがある方はTwitterとかでシェアーしてください

Slide 37

Slide 37 text

気になるあのサブルーチンのprototype、 コッソリ教えます

Slide 38

Slide 38 text

prototypeサブルーチン https://perldoc.perl.org/functions/prototype サブルーチンのprototypeを文字列として返す 組み込みサブルーチンのprototypeはうまく取得できないことがある (mapとか)

Slide 39

Slide 39 text

組み込みサブルーチンのprototype 実はperldoc perlsubに例示されている (全てではない) https://perldoc.perl.org/perlsub#Prototypes

Slide 40

Slide 40 text

実装を読みにいったほうが早い説 サブルーチン定義をがんばって探す XSなら PROTOTYPE: に書いてある けどREPLとかでサクッと見れるのはよいと思う

Slide 41

Slide 41 text

Deparseすると何が起こっているのか分かる説 perl -MO=Deparse は友達 どのように構文解析されたのかが分かる コンパイラの気持ちを知れば何が起こっているのかが分かるかも

Slide 42

Slide 42 text

prototypeを使う/使わない理由

Slide 43

Slide 43 text

prototypeを使う理由 DSL的に書きたい かっこよく書けるとかっこいい subって書きたくない あわよくばコンパイラの最適化を効かせたい

Slide 44

Slide 44 text

prototypeを使わない理由 ブロックのつもりで書いても無名関数になる 最近のPerlにはtry-catch構文が入っている なんでもprototypeを使えばよいというものではない (銀の弾丸ではない) use constantでよくない??

Slide 45

Slide 45 text

おさらい 読めるようになりましたね? sub foo ($) { } sub bar ($;$@) { } sub mymap (&@) { } sub zip (\@\@) { } sub foo () { 1 }

Slide 46

Slide 46 text

おわり prototypeによってPerlコードの表現力が広がる とはいえprototypeを濫用すればするほどよいというものではない 用法用量を守って楽しいprototypeライフを送りましょう { } は無名サブルーチンだというのを忘れずにいると罠を回避できるかも?

Slide 47

Slide 47 text

参考文献 ● https://perldoc.perl.org/perlsub ● https://perldoc.jp/docs/perl/5.36.0/perlsub.pod#Prototypes ● https://metacpan.org/pod/Try::Tiny ● https://papix.hatenablog.com/entry/2019/12/02/170611 ● https://metacpan.org/pod/Test2::V0 ● https://metacpan.org/pod/List::MoreUtils ● https://metacpan.org/pod/Scope::Guard ● https://perldoc.perl.org/functions/prototype

Slide 48

Slide 48 text

サンプルコード https://github.com/utgwkk/yapc-kyoto-2023-prototype で公開しています