Perlでtry-catch-finally文を使いたい(2022年版?)

以下の過去記事への自己レスのようなものです。

ryozi.hatenadiary.jp

コメントで教えていただいたり、try featureが追加されたりしていたのでこれを機に色々使ってみました。

あと、タイトルから「(Javaのような)try-catch-finally文をPerlで行う方法」と取れるような気がしており、おそらく記事の内容のような欠陥だらけの「try-catch-finally文っぽいものの作り方」を知りたい人は少数でしょうから、実際に行いたい人向けにどうするかをちょっと調べました。

要約

5.34にtry featureが追加されておりこれを使うべきです

古いPerlのバージョンであればXSを使うものの Syntax::Keyword::Try がちゃんと動いている気がします。

その他は(ちゃんと動くモノと比べると)うまく動かないケースもあるようだったので、注意して使いましょう。

調査

調査に使ったコードはGitHubに貼っておきました。

https://github.com/ryozi-tn/p5-try-catch-finally-2022

雑に調べた感じ以下の感じでした。

Try::Tiny Nice::Try Try::Harder Syntax::Keyword::Try Perl 5.34のtry feature
Perl バージョン 5.10 5.16 5.10 5.14 5.34
Cコンパイラ - 必要(依存パッケージ) - 必要 -
仕組み プロトタイプ宣言 Source Filter (Filter::Util::Call) Source Filter (Filter::Simple) XS? 組み込み?
try中のcaller × ◯変だが取れる ×finallyの末端
try中のreturn × ◯(1.3.3で確認)
catch中のreturn × ◯(1.3.3で確認)
catch中のdie ◯(1.3.3で確認) ?
last ラベル ◯ 警告が出るが動く ×
redo ラベル ◯ 警告が出るが動く × × finally後の処理が呼ばれない?

(catchの変数指定とか、finallyでreturnとか、finally無しとか足りない検証もありますが面倒なので...)

Try::Tiny

https://metacpan.org/pod/Try::Tiny

前の記事でコメントで「お前がやったことは再発明だよ」と紹介いただきました。

古き良きライブラリのようです。Perl v5.10でも動作します。

ただし、try中のreturnの挙動はtryのブロックを抜けるだけなので注意点があります。

Nice::Try

https://metacpan.org/pod/Nice::Try

前の記事でコメントで紹介いただきました。

Tiny::Tryとは異なり、文末のセミコロンが不要でより自然に書けます。

また、例外の変数名を指定できたり、例外クラスを定義してキャッチできるといった不思議なコード(後述)が特徴です。

Perl v5.16でも動作しますが、依存パッケージの都合でCコンパイラが必要になります。Pure Perl厨はご注意ください。

ただし、私がv1.3.1で試した感じではtry中のreturnの結果をを使わないコードだと異なる動きをします(バグっぽい?)

# サブルーチンの戻り値を代入(戻り値を使う)
$_ = (sub{ 
    try { return "OK" }catch{print "catch: Unreachable code\n";} finally {print "finally\n";} 
    print "Unreachable code?\n"; # 呼ばれない
})->();

# サブルーチンの戻り値を捨てる
(sub{ 
    try { return "OK" }catch{print "catch: Unreachable code\n";} finally {print "finally\n";}
    print "Unreachable code?\n"; # 呼ばれた
})->();

2023-01-13 追記: 作者様からコメントで修正された旨を受け再度確認し、1.3.3でtryとcatch中のreturnでvoid contextのとき(=戻り値を使わないとき)の挙動、catch中のdieでfinallyが呼ばれなかった点が修正されたことを確認しました

Try::Harder

https://metacpan.org/pod/Try::Harder

Nice::TryのSEE ALSOに書かれていて使ってみました。

Syntax::Keyword::Try をPure Perlで模擬したモノらしく、Syntax::Keyword::Tryがあればそちらを使うようで、無い場合はSource Filter(Filter::Simple)を使って頑張るようです。

redoラベルで期待していない挙動をしましたが、概ね良い動作をしているように見えます。

ただ、おそらくredo/lastなどのラベルを使ったコードを書いてると変な挙動をし始めます。コードをコピペして増やすとコンパイルエラーが起き始めました。(コピペしたコードをコメントアウトしても正規表現で置き換えるせいか影響を受けたり)

Syntax::Keyword::Try

https://metacpan.org/pod/Syntax::Keyword::Try

Nice::TryのSEE ALSOに書かれていたり、Try::Harderでも言及されていたので使ってみました。

XSを使うものの、Perl 5.34のtry featureと同じぐらい動きました。

Perl 5.34のtry feature

https://metacpan.org/release/XSAWYERX/perl-5.34.0/view/pod/perlsyn.pod#Try-Catch-Exception-Handling

こちらに書かれていることを鵜呑みにしました。

Perl 5.34.0 の try-catch を触ってみる

一番良い選択になるでしょう。

メモ

perl のdockerのベースイメージ

Official Imageが色々用意されています。古めなバージョンはタグ名が微妙に異なるので注意でv5.16は、FROM perl:5.16-slim-stretchとすればよいです。

*-slimは名前の通りサイズがスリムなイメージ。bashはあるので検証作業は行えるが、lessとかviとか当たり前のようにあるコマンドがないので注意です(編集系のコマンドはコンテナアプリでは不要なのでこんなもんでしょう)

Feature::Compat::Tryは?

https://metacpan.org/pod/Feature::Compat::Try

Syntax::Keyword::Tryと Perl 5.34のtry featureを切り替えて使うだけのようでした。 実際に試した感じ、5.34以前の古いバージョンではCコンパイラが必要ですが、5.34は不要でインストールできます。

Nice::TryやTry::Harderの catch($e) や セミコロン省略はどう実現しているのか?

PerlのSource filterを利用してます。Perlはコードをそのまま実行するのではなく、Source filterを通したコードを実行しているそうです。

Source filterは文字列しか扱えないので、Try::Harderは正規表現でマッチングしていたり、 Nice::TryではPerlの文脈を理解するためにPPIでパースしながら解釈しているようです。

Nice::Tryのソースコードを眺めて序盤で心が折れたのでメモしておきます。(頑張って構文を読んで処理を作り変えてはいそうだが…)

https://gitlab.com/jackdeguest/Nice-Try/-/blob/a84f0573/lib/Nice/Try.pm

  • L65: sub importの処理でソースフィルタを追加する。Filter::Util::Callのfilter_addを利用
  • L94-123: Filter::Util::Callの仕様よりfilterメソッドが呼び出される?ソースコード$codeへ読み込む(多分1行づつwhileで回して$codeへ追記している?)
  • L132-136): 読み取ったソースコードPPIでパースし、_parseメソッドへ渡す
  • L294-298: コードからtryで始まるStatement(文)を探してその参照を配列へ記録しておく
  • L308: try ~のコードの数だけ繰り返す
    • L321-330: tryかつコードブロックがあればtmp_refに詰め込みtmp_nodesをリセット。そうでない場合はtmp_nodesに詰め込む

よくわからなくなったのでここまで

Fitbit Inspire 3を買った

初スマートウォッチです。選んだ基準とか、使った感想とか。プログラマっぽいことはできてない

動機

別に私自身の健康のために買ったわけではない。

最近、親父の体調が良くなくて、独り暮らしなので孤独死を素早く検知しないと処理が大変だよなと思った(そこじゃないだろという声が聞こえる)。

で、人の死活監視に使えそうなのがないか考えて思いついたのがスマートウォッチだった。

なので、まずは使えそうかどうか調べるために買ってみた(まだそれらしい活動はできていない)

選定基準

  • バッテリーの持ちがよい
  • 死活監視に使えそうな機能があるか
    • アプリを作れる or Web APIでデータを取得できる
    • 死活監視に使えそうなメトリックが取れる(今回は心拍数がちゃんと取れる)
  • 変な製品は避ける。fitbitかapple watchしかなくなった。
  • お手頃価格
    • 1万前後

よかったところ

  • バッテリーが思っていたより持つ。1週間は付けっぱなしで使える。(1日10%弱ぐらいしか使わない)
  • 振動機能がある。起床アラームに使っている(起きれてないけど)
  • 数値化されるとやっぱり実感しやすくて良い
    • 睡眠時間の記録がつけられる。寝ている時間をしっかり記録できていてびっくりした。
    • 歩数計がある
  • スマホのSMS通知を飛ばしてくれる。ワンタイムパスワードとかの確認に地味に便利だった(セキュリティ的にはアタックサーフェスが増える問題にもなりそうだが)

睡眠時間の記録はスマートフォンはこんな感じに見れる。

生活リズムが崩壊していたり、寝つけず寝坊している様子がよくわかる。

fitbitじゃなくても良かったのでは?という感じはある。

その他

iPhone SE2 にFitbitのアプリを入れて使っている。ユーザ登録が必要。fitbitに自身の生体データを見られてしまっていることになるが気にしない。bluetoothで繋がっている。単体ではWifiとか繋げられないのでスマートフォンが必須っぽい。

心拍数等のメトリックはクラウドに送られてWeb APIで取れるらしい。この辺りはもう少し調べたい(いつまでのデータが取れるか、同期のタイミングなど)

https://www.fitbit.com/dev

C#のオンラインコンパイラーの紹介

初心に帰ってちょっとした学びとかも書いていきたい。

コードをさっと書いて試したいことはよくある。Webにはオンラインコンパイラーを提供してくれるサービスが存在し、ありがたいことにFreeで使えるものも多い。今回はC#について書く。

個人的に求めるているもの

  • コードがURLで共有できる (アクセスしてすぐ試せる、すぐフォークしていじれる)
  • 無料
  • なるべくログイン不要
  • できれば複数ファイルを扱える
  • できれば補完が効く

多分探せば他にもあるとは思うが、一旦以下を紹介する。

.NET Fiddle

https://dotnetfiddle.net/

.NET Fiddleはjsfiddleにインスパイアされたオンラインコンパイラー。

  • 補完は少々機能する
  • 様々なランタイムバージョン(最新の .NET 7 も可能)
  • 結果を含めた共有リンクが可能(前はログインが必要だった気がしたが今は匿名で可能)
  • Nugetに対応
  • SQL Serverを使ったDB操作に対応
  • 動作させるだけでなく、IL(中間言語)も見れる

かなり高機能で、C#のお試しはとりあえずこれでよさそう。

NugetでMicrosoft.Extensions.DependencyInjectionのコードを試し書きしたもの。

https://dotnetfiddle.net/s5UVOB

Wandbox

C# に限らない様々なオンラインコンパイラーを提供している。

https://wandbox.org/

  • ログイン不要
  • 結果を含めた共有リンクが可能
  • コードはGistで管理、複数ファイル可能

他と比べると機能は劣るが、オンラインコンパイラであると嬉しい最低限の機能はそろっており必要十分。

.NET 5で止まっており、.NET 6や.NET 7の対応が待たれる。やる気が起きたら次を参考にPR送りたい...

dotnetcore 追加 · melpon/wandbox-builder@ba0676b · GitHub

Try dot net

https://try.dot.net/

https://dotnet.microsoft.com/ja-jp/platform/try-dotnet

  • ログイン不要
  • ブラウザ上で動作(wasm)
  • 補完が.NET Fiddleよりもリッチ
  • コードはGistで管理、複数ファイル可能
  • JSから操作できるAPIを持つ
  • Microsoftが持つドメインで動作している
  • Monoを使っている

Mono 6.5.0あたりで動作しているようで、C# 9 以降の機能(record等)は試せなかった。

Replit

https://replit.com/

ユーザ登録が必要だが、非常に多機能。ちゃんとプロジェクトも扱えるし、ちょっとした統合開発環境

動作が若干もっさり。個人的にはここまで動作するのを求めるなら、ローカルでやってしまいたい気はする。

.NET Fiddleと同じコードを動かしたもの(作者のログイン時はエディタ画面だが、それ以外は実行ができるだけ)

https://replit.com/@RyoziYoda/Example#main.cs