Blog::kobaken

prove t/foo/bar/baz.t

Perlのコンテキストと仲良くなるツールを書いた

f:id:kfly8:20210508142036p:plain:w600

metacpan.org


Perlのコンテキストに戸惑う場面はあると思います。

my @a = ("apple","banana");
if (@a) { ... }

「あれ?なんでifに配列を入れて動く?」  
「それは、真偽値コンテキストで解釈されるね」  
「なるほど?」 

こういった戸惑いが少しでも減ることを目的に、Contextual::Diagというモジュールを書きました*1

Perlのコンテキストについて

Perlのコンテキストは「内部的に多態なデータを、評価次第で、型解釈を決める」概念で、
例えば、$str eq "hello"は文字列が一致するか比較している場面なので、Perl$strを文字列として評価しようとします。
プログラミング言語によっては、toString, to_sといったメソッドを明示的に呼び出しますが、Perlの場合は、eq に反応しています。

詳しいことは、perldataを読むか、この辺りのスライドがおすすめです。

使い方

コンテキストを知りたい箇所に、contextual_diag 関数を差し込めば、コンテキストを警告してくれます。 次のコードは、ハッシュリファレンスの値を評価する時のコンテキストを探っています。

use Contextual::Diag;

# これを・・・
my $a = { key => 'value' };

# こうする
my $a = { key => contextual_diag 'value' };

# すると、リストコンテキストだと教えてくれる
# => wanted LIST context

もう少し詳しい話

Contextual::Diagでは、まず、wantarrayでSCALAR,LIST,VOIDの大区分のコンテキストを切り分けます。次に、SCALARコンテキストの場合は、返り値をオブジェクトでくるんでやり、overloadを使って、文字列評価、数値評価、配列デリファレンスされた場合の処理をフックしています。オブジェクトリファレンスのケースだけは、overloadでフックしきれないので、AUTOLOADを利用しています。

まとめると、次のコンテキストの診断をします。

診断の一覧

1. SCALAR コンテキスト
   - CASE: Scalar value
      - BOOL評価 e.g. `if ($value)`
      - NUM評価 e.g. `$value + 1`
      - STR評価 e.g. `$value . "hello"`
   - CASE:  Scalar reference
      - SCALARREF e.g. `$$value`
      - ARRAYREF e.g. `$value->[0]`
      - HASHREF e.g. `$value->{key}`
      - CODEREF e.g. `$value->()`
      - GLOBREF e.g. `*{$value}->{CODE}`
      - OBJREF e.g. `$value->hello()`
2. LIST コンテキスト
3. VOID コンテキスト

さいごに

このツールで、Perlのコンテキストと仲良くなる手助けができたら、嬉しいです。 良かったら使ってみてください。

*1:書いたのは1年半前だったけれど、CPANに週末あげた