理系学生日記

おまえはいつまで学生気分なのか

Moose における再帰構造を持った型制約

Moose では ArrayRef[hoge] という形で、型 hoge の配列リファレンスという型制約が定義できる。
hoge には Int や Str、あるいは Moose で定義したクラス名などが入るが、'ArrayRef[hoge]' 自体も入れることができる。これを繰り返すと当然ながら

ArrayRef[Arrayref[Int]]

であったり、

ArrayRef[ArrayRef[Arrayref[Int]]]

などといったものが生み出せる。

では、Moose はこのような再帰構造を持った型制約にどれだけ耐えることができるのかという限りなくどうでも良い話がこのエントリの主題であって、結論としては予想通り Perl が再帰呼び出しを許す限りにおいて無限という結果になる。

以下、検証。
5 階層とかは余裕である。

% ./test-recursive.pl 5
type:  ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[Int]]]]]
array: [[[[[1]]]]]

98 回 Arrayref を重ねても大丈夫。

% ./test-recursive.pl 98
type:  ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[Int]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
array: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]

99 回重ねるとアウト。Deep recursion のエラーについては、perldoc perldiag を参照のこと。

% ./test-recursive.pl 99
type:  ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[ArrayRef[Int]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
array: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
Deep recursion on subroutine "Moose::Util::TypeConstraints::find_or_create_isa_type_constraint" at /Users/kiririmode/perl5/lib/perl5/Moose/Meta/TypeConstraint/Parameterizable.pm line 47.
Deep recursion on subroutine "Moose::Util::TypeConstraints::find_or_parse_type_constraint" at /Users/kiririmode/perl5/lib/perl5/Moose/Util/TypeConstraints.pm line 204.

検証コードは以下。

use feature qw(say);

sub make_recursive {
    my ($num, $append, $src) = @_;
    my $ret = $src;
    for ( 1 .. $num ) {
        $ret = "${append}[$ret]";
    }
    $ret;
}

my $c  = do { my $arg = shift; chomp $arg; $arg };
my $type  = make_recursive( $c, 'ArrayRef' => 'Int' );
my $array = make_recursive( $c, '' => '1' );
say "type:  $type";
say "array: $array";

eval {
    package Test;
    use Moose;
    has 'array' => ( is => 'rw', isa => $type );
};

my $t = Test->new( array => eval "$array" );