A note about ClassName type

以前 id:Craftworks さんとMooseのClassName型について話した件*1で思うところがあったのでメモしておく。
まず,現在のClassName型の定義は以下のようになっている。

sub ClassName {
    return Class::MOP::is_class_loaded($_);
}

これは確かにおかしい。クラスがロード済みかどうかへの関心は,アプリケーション毎に違うはずだ。実際,CMOP/Mooseの内部ではクラス名をとるアトリビュートを大量に定義しているが,それらのアトリビュートを使う場合はほとんどのケースでクラスのロードを自前で行っている。つまり,それらアトリビュートの論理的な制約*2は現在のClassNameとは異なる。

したがって,ClassNameの定義は以下のほうが使いやすいのではないかと考えられる。

sub ClassName {
    return defined($_) && /\A \w+ (?: ::\w+) \z/xms;
}

必要であれば,これに加えて ClassName.loaded などのサブタイプを提供するほうがいいように思われる。

ところが,Type as State, Coercion as Hookを応用して,現在の定義をうまく利用することができることに気付いた。

#!perl
package A;
use Any::Moose;
use Any::Moose '::Util::TypeConstraints';

subtype 'ClassName.autoload', as 'ClassName';

coerce 'ClassName.autoload',
    from 'Str', via { Any::Moose::load_class($_); $_ };

# demo

my $ClassName = find_type_constraint('ClassName.autoload');

print Any::Moose::is_class_loaded('File::Spec'), "\n";
print $ClassName->coerce('File::Spec'), "\n";
print Any::Moose::is_class_loaded('File::Spec'), "\n";
__END__
結果:
               # 偽(空文字列)
File::Spec     # クラス名
1              # 真

だから現在の定義もそれなりに使えるのかもしれない。

*1:結局まだ報告してませんが… orz

*2:実際にはそれらのアトリビュートは型制約をもたないが