Checkfunctions
チェック関数群は":check"インポートタグによって導入できます。これらはある値
の型が目的の型であれば真を,そうでなければ偽を返します。
また,これらの関数はオーバーロードマジックも調べます。たとえば,"${}"が
オーバーロードされているオブジェクトは,スカラーリファレンスとして扱われます。
リファレンスの型チェックをする関数は,オブジェクトリファレンスに対しては,
オーバーロードされていない限り常に偽を返します。
これは,オブジェクトの実装に依存するコードを書かないようにするためです。
is_scalar_ref(value)
スカラーリファレンスかどうかのチェックを行います。
is_array_ref(value)
配列リファレンスかどうかのチェックを行います。
is_hash_ref(value)
ハッシュリファレンスかどうかのチェックを行います。
is_code_ref(value)
コードリファレンスかどうかのチェックを行います。
is_glob_ref(value)
グロブリファレンスかどうかのチェックを行います。
is_regex_ref(value)
"qr//"によって作られる正規表現かどうかのチェックを行います。
is_instance(value, class)
classのインスタンスかどうかのチェックを行います。
"Scalar::Util::blessed($value) && $value->isa($class)"というコードと ほぼ等価です。
classが未定義値またはリファレンスであれば致命的エラーとなります。
is_invocant(value)
valueに対してメソッドを起動できるかどうかをチェックします。
is_value(value)
valueがプリミティブ値かどうかをチェックします。すなわち,定義済みであり,
リファレンスではなく,型グロブでもなければ真を返します。
この関数(および"is_string"/is_number()/is_integer())は,
オブジェクトリファレンスに対しては常に偽を返します。
たとえvalueが文字列化/数値化/真偽値化オーバーロードメソッドを
持っていたとしても,それはプリミティブ値としては判断しません。
この関数には検証を行う対応関数がありません。
is_string(value)
valueがプリミティブ値であり, かつ文字列化したときに1文字以上の内容を持つ値かどうかをチェックします。
"do{ is_value($value) && length($value) > 0 }"と同じです。
この関数には検証を行う対応関数がありません。
is_number(value)
valueが数値かどうかをチェックします。 ここで数値とは,数値コンテキスト(たとえば"sprintf '%g',
$value") で警告を出さずに数値に変換可能であり,
かつPerlプログラム中にリテラルとしておくことができる値という意味です。
すなわち,この関数はScalar::Util::looks_like_number()と異なり,
"Infinity"や"NaN"はリテラルとしてプログラム中に置くことはできないため,
数値として扱いません。また,数値化したときに警告を出さない例外である "0 but
true"も同じ理由で数値として扱いません。
この関数には検証を行う対応関数がありません。
is_integer(value)
valueが整数かどうかをチェックします。これはis_number()の判定に加えて,
整数値かどうかをチェックします。
この関数には検証を行う対応関数がありません。
Validatingfunctions
検証関数は":validate"タグによって導入できます。これらはチェック関数と 同じ方法でチェックを行います。
ただし,その結果が真であれば第一引数をそのまま返し, 偽であれば致命的エラーとなります。
これらの関数もオーバーロードマジックを考慮します。
scalar_ref(value)
スカラーリファレンスかどうかの検証を行います。
array_ref(value)
配列リファレンスかどうかの検証を行います。
hash_ref(value)
ハッシュリファレンスかどうかの検証を行います。
code_ref(value)
コードリファレンスかどうかの検証を行います。
glob_ref(value)
グロブリファレンスかどうかの検証を行います。
regex_ref(value)
"qr//"によって作られる正規表現かどうかの検証を行います。
instance(value, class)
classのインスタンスかどうかの検証を行います。
classが未定義値またはリファレンスであれば致命的エラーとなります。
invocant(value)
valueに対してメソッドを起動できるかどうかの検証を行います。
valueがクラス名である場合,そのクラス名を正規化した文字列を返します。
すなわち,"::Foo"や"main::Foo"を与えると"Foo"を返します。
Micellaneousutilities
その他,個別にインポートできるいくつかのユーティリティ関数があります。
anon_scalar()
"undef"を参照する匿名スカラーリファレンスを生成します。
anon_scalar(value)
valueのコピーを参照する匿名スカラーリファレンスを生成します。
これは"do{ my $tmp = $value; \$value; }"というコードと等価です。
neat(value)
valueを表示に適するよう整形した文字列を返します。 "do{ defined($value) ? qq{"$value"} : 'undef'
}"を置き換える機能 として提供されますが,より高機能です。
get_stash(invocant)
invocantのスタッシュ stash,つまりシンボルテーブルハッシュが 存在すれば,そのスタッシュを返します。
invocantがオブジェクトリファレンスであれば,そのオブジェクトのパッケージの スタッシュを返します。
invocantがパッケージ名であり,そのパッケージが既に存在すれば, そのパッケージのスタッシュを返します。
install_subroutine(package, name => subr [, ...])
サブルーチンsubrをpackageにnameとしてインストールします。
"do{ no strict 'refs'; *{$package.'::'.$name} = \&subr; }"というコードと
ほぼ等価です。さらに,subrが匿名サブルーチンであれば,packageに
名前付きサブルーチン&package::nameとして命名します(ただし,Pure
Perl版のコードでは匿名サブルーチンの命名は行いません)。
サブルーチンを再インストールするときは,"no warnings 'redefine'" ディレクティブを使ってください。
no warnings 'redefine';
install_subrouitne($package, $name => $subr);
packageかnameが未定義値またはリファレンスであれば致命的エラーとなります。
subrがコードリファレンスでないときも致命的エラーとなりますが, オーバーロードマジックは考慮されます。
この関数は"no strict
'refs'"を必要としないため,strict無効化の誤謬を犯す危険性がありません。strict無効化の誤謬とは,以下のような状況を指します。
my $property = ...;
# ...
no strict 'refs';
# simple read-only accessor
*{$pkg . '::' . $sub_name} = sub{
my($self) = @_;
return $self->{$property};
}
これはオブジェクトのプロパティを参照するアクセサを生成するコードです。このアクセサは,正しく使う限りでは問題はありません。
しかし,このアクセサをクラスメソッドとして呼び出すと,問題が顕在化します。
つまりそのとき$selfに入っているのはクラスを表す文字列であり,
"$self->{$property}"というコードはシンボリックリファレンスと解釈され,
このアクセサが定義されたパッケージのグローバル変数としてデリファレンスされます。
これは多くの場合,単に"undef"を返すだけでしょう。 "<use strict
'refs'">はまさにこのような誤ったシンボリックリファレンスの
デリファレンスを検出するために用意されている機能なのですが,ここではその恩恵を
得ることができず,デバッグの難しいコードを生成してしまいます。
このケースでstrictの恩恵を得るためには,以下のように無名関数内で再度 "use
strict"を有効にする必要があります。
no strict 'refs';
*{$pkg . '::' . $sub_name} = sub{
use strict 'refs';
my($self) = @_;
return $self->{$property};
}
そこで,install_subroutine()を使うとも"strict"を使用する必要がなくなります。
install_subroutine $pkg => (
$sub_name => sub{
my($self) = @_;
return $self->{$property};
},
);
このstrict無効化の誤謬については,"18.10" in "Perlベストプラクティス" 「制約の無効化-制約または警告を無効にする場合は,明示的に,段階的に,最も狭いスコープで行う」 に解説があります。
uninstall_subroutine(package, name [=> code], ...)
サブルーチンnameをパッケージpackageから削除します。
"undef &subr"が&subrを未定義にして型グロブのコードスロットを
そのままにするのに対して,"uninstall_subroutine"は型グロブを
シンボルテーブルから削除し,コードスロットを以外の値をシンボルテーブルに 戻します。
この挙動は"namespace::clean"や"constant::lexical"を実現するためのものです。
nameに対してcodeが与えられている場合は,&package::nameがcodeである
場合のみ削除します。すなわち,以下の二つのコードは等価です。
uninstall_subroutine($pkg, $name) if \&{$pkg . '::' . $name} == $code;
uninstall_subroutine($pkg, $name => $code);
この関数はSub::Delete::delete_sub()と同じアルゴリズムに基づいていますが,
複数のサブルーチンを一度に削除できます。
get_code_info(subr)
サブルーチンsubrのパッケージと名前のペアを返します。
これはSub::Identify::get_code_info()とほぼ同じ機能です。
ただし,スカラーコンテキストでは完全修飾名を返します。
subrの名前が不明なときは,リストコンテキストでは空リストを,
スカラーコンテキストでは"undef"を返します。
get_code_ref(package, name)
\&package::nameが存在すれば,それを返します。 これは"do{ no strict 'refs'; *{$package . '::' .
$name}{CODE} }" に似ていますが,\&package::nameが存在しない場合でも *package::nameを生成しません。
第三引数として"-create"を与えると,\&package::nameが存在しなくても スタブを生成してそれを返します。
これは"do{ no strict 'refs'; \&{$package . '::' . $name} }"と同じです。
curry(subr, args and/or placeholders)
サブルーチンsubrのカリー化を行います。 つまり特定の引数を固定したクロージャを生成します。
argsand/orplaceholdersには,固定する引数か,カリー化サブルーチンの引数に
置き換えられるプレースホルダを渡します。プレースホルダには,添え字xを参照
する"\x"と,"\x"で参照した最大の添え字の以降の引数リストを参照する *_があります。
たとえば,以下の$closureと$curriedは同じ機能を持つサブルーチンとなります。
my $class = 'Foo';
$closure = sub{ is_instance($_[0], $class) };
$curried = curry \&is_instance, \0, $class;
$closure = sub{ install_subroutine($class, @_) };
$curried = curry \&install_subroutine, $class, *_;
なお,*_は"\x"で参照しなかった引数リストではないので注意してください。 たとえば,"curry(\&subr, *_,
\1)->(0, 1, 2, 3)"というカリー化では, "subr(2, 3,
1)"が呼び出され,カリー化されたサブルーチンに与えられた $_[0](つまり0)が無視されます。
カリー化はクロージャよりも生成・呼び出しが高速です。
より詳しいサンプルコードがData::Util::Curryにあります。
modify_subroutine(subr, modifier_type => [subroutines], ...)
サブルーチンsubrをmodifier_typeにしたがってsubroutinesで修飾し,
無名関数modified_subrとして返します。
modifier_typeには"before", "around", "after"があり,"before"は
subrの呼び出し前に,"after"はsubrの呼出し後に,modified_subrに
与えられた引数で呼び出されます。"before"と"after"の戻り値は捨てられます。
"around"はsubrの入出力をフィルタリングするための修飾子です。
その際,呼び出順は,"before"と"around"は後で定義されたものが先に呼び出され
(last-defined-first-called),"after"は先に定義されたものが先に呼び出されます(first-defined-first-called)。この呼び出し順はsubroutine_modifier()でも同じ
です。
たとえば:
$modified = modify_subroutine(\&foo, around => [sub{
my $next = shift;
do_something();
goto &{$next}; # continuation
}]);
$modified->();
$modified = modify_subroutine(\&foo,
before => \@befores,
around => \@arounds,
after => \@afters,
);
$modified->();
XSによる実装では,サブルーチン修飾子のコストが非常に安くなっています。
このディストリビューションに付属しているexample/lib/Method/Modifiers.pm
(modify_subroutine()/subroutine_modifier()のデモ)のベンチマーク
benchmark/methext_bench.plによれば,メソッド修飾のコストはほぼ次のようになります:
with before modifier: 100% slower
with after modifier: 100% slower
with around modifier: 200% slower
特に,"before"と"after"は"SUPER::"疑似クラスによってメソッドを拡張するよりも高速です。
各修飾子については,"Method Modifiers" in Class::MOP::Classに
詳しい解説があります。Class::Method::Modifiersにも解説があります。
このモジュールが提供するAPIはこれらのモジュールより低水準ですが, 機能には互換性があります。
subroutine_modifier(modified, modifier_type => subroutines, ...)
modify_subroutine()で生成したmodifiedを操作します。
引数をmodifiedのみ渡した場合は,そのmodifiedがmodify_subroutine()で
生成されたものかどうかを示す真偽値を返します。
if(subroutine_modifier $subr){
# $subrは修飾子つきサブルーチン
}
modifiedとmodifier_type("before", "around", "after")
を渡すと,そのmodifier_typeに応じた修飾関数を返します。
@befores = subroutine_modifier $modified, 'before';
このほか,更に関数のリストを渡した場合には,modifiedのmodifier_typeに その関数を追加します。
subroutine_modifier $modified, before => @befores;
mkopt(input, moniker, require_unique, must_be)
inputを元に名前と値のペア配列からなる配列リファレンスを作成します。
これはData::OptList::mkopt()に似ています。それに加えて,must_beは
名前と型のペアからなるハッシュリファレンスでもかまいません。
For example:
$array_ref = mkopt([qw(foo bar), baz => [42]], 'moniker');
# $array_ref == [ [foo => undef], [bar => undef], baz => [42] ]
mkopt_hash(input, moniker, must_be)
inputを元にハッシュリファレンスを作成します。
これはData::OptList::mkopt_hash()に似ています。それに加えて,must_beは
名前と型のペアからなるハッシュリファレンスでもかまいません。
For example:
$hash_ref = mkopt([qw(foo bar), baz => [42]], 'moniker');
# $hash_ref == { foo => undef, bar => undef, baz => [42] }
Errorhandling
検証関数によって放出される致命的エラーは,"Data::Util::Error"モジュールによって変更することができます。
package Foo;
use Data::Util::Error sub{ Foo::InvalidArgument->throw(@_) };
use Data::Util qw(:validate);
# ...
このエラーハンドラはパッケージ毎に設定され,そのパッケージ内で"Data::Util"が発生させるエラーにはそのエラーハンドラが使われます。