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

prototype大全 / YAPC::Kyoto 2023

prototype大全 / YAPC::Kyoto 2023

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

utagawa kiki

March 19, 2023
Tweet

More Decks by utagawa kiki

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. try-catchから始めるprototype入門

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  12. prototypeの例
    sub foo ($) { }
    sub bar ($;[email protected]) { }
    sub mymap (&@) { }
    sub zip (\@\@) { }
    sub foo () { 1 }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. こんなにあるぞ!! prototype

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  31. 身近に潜む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]], ... のリストを返す

    View Slide

  32. 身近に潜む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

    View Slide

  33. 身近に潜む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

    View Slide

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

    View Slide

  35. 身近に潜む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.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  42. prototypeを使う/使わない理由

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  47. 参考文献
    ● 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

    View Slide

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

    View Slide