【Perl】MooseX::Declareで、Mooseをより手軽に
投稿日: / 更新日:
この記事は2年以上前に書かれたものです。情報が古い可能性があります。
Mooseでは、オブジェクトのプロパティ(Mooseではアトリビュートといいます)の型チェックは簡単に記述することができるのですが、一方でメソッドの引数については、従来のPerlサブルーチンの実装と同じため、自前でバリデーションを実装する必要があります。
以下は、”モダンPerl入門”(翔泳社)に掲載されているEchoサーバの実装例に、引数のバリデーション処理等を加えたものです。バリデーションについては、MooseX::Params::Validateを使用して、できるだけ少ないコード量で実現したつもりですが、全てのメソッドの入り口に似たようなコードを書くことになり、いまいち冗長さが拭えない感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# EchoServer.pm package EchoServer; use Moose; use namespace::autoclean; use MooseX::Types; use MooseX::Params::Validate; BEGIN { class_type SOCK => { class => "IO::Socket::INET" }; } has 'address' => ( is => 'rw', isa => 'Str', required => 1, default => "127.0.0.1", ); has 'port' => ( is => 'rw', isa => 'Int', required => 1, default => 9999, ); has 'server_socket' => ( is => 'rw', isa => 'IO::Socket', ); __PACKAGE__->meta->make_immutable; use IO::Socket::INET; sub bind { my ($self) = pos_validated_list( \@_, { isa => __PACKAGE__ }, ); my $socket = IO::Socket::INET->new( Listen => 5, LocalAddr => $self->address, LocalPort => $self->port, Proto => 'tcp', ); die "Failed to create socket: $@" unless $socket; $self->server_socket($socket); } sub run { my ($self) = pos_validated_list( \@_, { isa => __PACKAGE__ }, ); my $socket = $self->server_socket; while (my $client = $socket->accept) { $self->proc_req($client); } } sub proc_req { my ($self, $client) = pos_validated_list( \@_, { isa => __PACKAGE__ }, { isa => 'SOCK' }, ); while (not $client->eof) { my $req = $self->read_req($client); $self->write_res($client, $req); } } sub read_req { my ($self, $client) = pos_validated_list( \@_, { isa => __PACKAGE__ }, { isa => 'SOCK' }, ); scalar <$client>; } sub write_res { my ($self, $client, $req) = pos_validated_list( \@_, { isa => __PACKAGE__ }, { isa => 'SOCK' }, { isa => 'Str' }, ); print $client $req; } package main; sub main { my $s = EchoServer->new; $s->bind; $s->run; } main() unless caller; __END__ |
さて、この度読んだ”Modern Perl”という電子書籍で、MooseX::DeclareというCPANモジュールがあるのを知ったのですが、これを使用すると、メソッドに(C++やJavaなどと似たような)シグネチャを付与することができます(メソッドのオーバーロードができないので、実際には似て非なるものですが…)。これにより、メソッドが受け取った引数のバリデーションを宣言的に記述できるようになります。以下は、このモジュールを使って書き直したコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# EchoServer2.pm use MooseX::Declare; use MooseX::Types; BEGIN { class_type SOCK => { class => "IO::Socket::INET" }; } class EchoServer2 { use IO::Socket::INET; has 'address' => ( is => 'rw', isa => 'Str', required => 1, default => "127.0.0.1", ); has 'port' => ( is => 'rw', isa => 'Int', required => 1, default => 9999, ); has 'server_socket' => ( is => 'rw', isa => 'IO::Socket', ); method bind { my $socket = IO::Socket::INET->new( Listen => 5, LocalAddr => $self->address, LocalPort => $self->port, Proto => 'tcp', ); die "Failed to create socket: $@" unless $socket; $self->server_socket($socket); } method run { my $socket = $self->server_socket; while (my $client = $socket->accept) { $self->proc_req($client); } } method proc_req(SOCK $client) { while (not $client->eof) { my $req = $self->read_req($client); $self->write_res($client, $req); } } method read_req(SOCK $client) { scalar <$client>; } method write_res(SOCK $client, Str $req) { print $client $req; } } sub main { my $s = EchoServer2->new; $s->bind; $s->run; } main() unless caller; __END__ |
ご覧のとおり、method
というキーワードの後にメソッド名と引数の型、及び受け皿となる変数名を記述することで、C++やJavaなどのように、引数が自動的にその変数へ代入され、同時に型のチェックが行われます(ついでに、クラス定義にclass
というキーワードが使えるようになり、名前空間の始末や不変化なども自動的に行ってくれます)。これで、お決まりのコードを書く必要がなくなり、コードも大分すっきりしました(ちなみに、Mooseではアトリビュートのアクセサ/ミューテータすら記述する必要がありません)。このモジュールを使用することで、Mooseでのプログラミングがより一層ラクに行えそうです。
以上、Perlに関する簡単な情報をご紹介しました。