id:papix です. この記事は, Perl Advent Calendar 2019の2日目の記事です. 昨日は,
id:karupanerura さんの「2019年の最先端のPerl開発ボイラープレート」でした.
今日は, 昨日のエントリでも触れられていたSyntax::Keyword::Tryについて, さっくりとアレコレ綴りたいと思います.
Perlと例外処理
Perlで例外処理をするにあたって, 例えばJavaのようなtryやcatchといった仕組みは言語仕様として提供されていません.
そのため, Perl Monger達はtry/catchのような機能を提供するモジュールを用意して対処してきました.
その中でも著名なものの1つは, Try::Tinyでしょう.
Try::Tinyを使えば, 例外(die)が起こるコードについて, 次のように書くことができます.
use Try::Tiny; sub run { try { die "!!!"; } catch { warn "recovery"; }; } run();
tryで囲まれたコードで例外が発生した場合, Try::Tinyはそれをよしなにcatchしてくれて, catchで囲まれたコードを実行してくれる訳です.
Try::Tinyの罠
さて, Try::Tinyには, 何だかんだで誰もが1度はハマる罠が存在します. それが次のコードです.
use Try::Tiny; sub run { try { # ... 例外が起こるかもしれない処理 ... } catch { warn "recovery"; return 1; }; # ... 何かしらの処理 ... return 2; } my $result = run(); print "$result\n";
このコードを実行したとき, print "$result\n"; は何と表示されるでしょうか? コードを書いた人の意図としては,
tryで囲まれたコードで例外が発生すれば,catchで囲まれたコードが実行され, 結果としてrunは1を返すtryで囲まれたコードで例外が発生しなければ,# ... 何かしらの処理 ...の部分にある何かしらのロジックが実行され, 結果としてrunは2を返す
...と考えていそうです. しかしながら, 例えば # ... 例外が起こるかもしれない処理 ... を, 実際に例外が起こるようなコード(例えばdie("!!!"))にしたとしても, このコードの実行結果(= print "$result\n";の出力)は, 2になります.
これは何故でしょうか? 結論から言えば, Try::TinyはPerlの既存の仕組み(プロトタイプなど)を利用して, 強引にtry/catch風の構文を再現しているから... ということになります. 実は, 先のコードは下記のコードと等価なのです.
try (
sub {
# ... 例外が起こるかもしれない処理 ...
},
catch (
sub {
warn "recovery";
return 1;
},
),
);
そのため, return 1はTry::Tinyが提供するcatchという関数の第一引数に渡るサブルーチンリファレンスの返り値となり, 最終的にこの値はTry::Tinyが提供するtryという関数の返り値となるわけです.
また, Try::Tinyの場合, 最後のコードブロックに;をつける必要があるというのも, よくある罠と言えるでしょう.
use Try::Tiny; sub run { try { # ... 例外が起こるかもしれない処理 ... } catch { warn "recovery"; return 1; } # ← ここに ; が必要!!! # ... 何かしらの処理 ... return 2; }
Syntax::Keyword::Try
さて, そういったTry::Tinyの問題を解決する, Syntax::Keyword::Tryというモジュールがあります.
このモジュールを使うと, Perlにおける例外処理を次のように実装することができます:
use Syntax::Keyword::Try; sub run { try { # ... 例外が起こるかもしれない処理 ... } catch { warn "recovery"; return 1; } # ... 何かしらの処理 ... return 2; } my $result = run(); print "$result\n";
末尾の;が不要だったり, # ... 例外が起こるかもしれない処理 ...で例外が起おきた時も, 関数runは当初意図していた通り, 1を返します(当然, 例外が発生しなければ return 2 によって2を返す).
Syntax::Keyword::Tryには, Try::Tinyに潜む罠が存在しないのです.
「キーワードプラグイン」
さて, このモジュールは, Perlの"キーワードプラグイン"という仕組みを使って実装されています. キーワードプラグインはPerl 5.12(2010年リリース)から実装された機能で, Perlのパーサーにフックを設定し, 構文を拡張できるようになっています.
Syntax::Keyword::Try以外にも, キーワードプラグインを利用したモジュールは公開されていて, 例えばFunction::Parameters(関数の定義をより良い形で記述できる), Keyword::Boolean(true/falseを提供する), Switch::Plain(switch構文を提供する)などがあります.
id:charsbar さんが2016年に参加した, London Perl Workshop 2016で, Syntax::Keyword::Tryを開発した, Paul Evans氏の発表があったそうですが,
Perlをもっとよくしていくには、公開されている機能をギリギリまで使い倒して、できる人たちに「そんな無茶なやり方よりもっといい方法がある」と言わせるといい
...と仰っていたそうです. キーワードプラグインによって今のPerlを拡張し, 日々の開発に役立て, それを通して未来のPerlに貢献する... というのは, とてもおもしろそうですね.
次回予告
...というわけで, キーワードプラグインの実装までご紹介出来ればよかったのですが, ちょっと準備に時間が足りず... 今回はここまでとさせてください. 改めて, Perl Advent Calendar 2019の16日目にて, 「キーワードプラグインを使ったモジュールの実装について」をご紹介させてもらえればと思います!!!!!!
Perl Advent Calendar 2019, 明日の担当は, take_3さんです.