Type as State, Coercion as Hook
MooseのTypeConstraintは,型というよりはあるデータの性質を表現したものだと考えられる。また,TypeCoercionは,ある型(=あるデータの性質)に対してフックを掛けるメカニズムと考えられる。
このように考えると,Coercionを利用していろいろと面白いことができるのではないかと思う。
たとえば,以下のようにCoercionを利用してエンコーディングを推測することができる*1。
#!perl package E; use Any::Moose; use Any::Moose '::Util::TypeConstraints'; use Encode qw(encode decode FB_QUIET); subtype 'UniStr', as 'Str', where { utf8::is_utf8($_) }; # define types represent an encoding subtype 'utf8', as 'Str', where { decode('utf8', $_, FB_QUIET) ne '' }; subtype 'shift_jis', as 'Str', where { decode('shift_jis', $_, FB_QUIET) ne '' }; subtype 'euc_jp', as 'Str', where { decode('euc-jp', $_, FB_QUIET) ne '' }; coerce 'UniStr', from 'utf8', via { decode('utf8', $_) }, from 'euc_jp', via { decode('euc-jp', $_) }, from 'shift_jis', via { decode('shift_jis', $_) }, ; # testing # "ko-n-ni-chi-wa-!" in Hiragana my $us = "\x{3053}\x{3093}\x{306b}\x{3061}\x{306f}\x{ff01}\n"; my $utf8 = encode('utf8', $us); my $sjis = encode('shift_jis', $us); my $eucjp = encode('euc-jp', $us); my $UniStr = find_type_constraint('UniStr'); binmode STDOUT, 'utf8'; print $UniStr->coerce($us); print $UniStr->coerce($utf8); print $UniStr->coerce($sjis); print $UniStr->coerce($eucjp); __END__
utf8-flagged文字列はもちろん,様々なエンコーディングのバイト列を渡しても,四回正しく「こんにちは」と出力される*2。
UniStrとそれに対するcoerce()の定義は,「coerce()に渡された文字列がutf8-flaggedでなければそれぞれのエンコーディングでデコードを試し,成功すればそのエンコーディングと見なせ」という意味である。そして成功したエンコーディングがあれば,対応するvia{ ... }が実行され,実際のデコードが行われる*3。ここでは,"shift_jis"などのsubtypeは純粋にcoercionのためだけに定義している。
また,最近のMooseにはmatch_on_typeというユーティリティ関数がある。これでもいろいろ遊べそうだ。