Perl に class 構文がやってきた

Perl は以前からオブジェクト指向プログラミングをサポートしてきました。 しかし、Perl そのものはオブジェクト指向プログラミング言語ではないため、 オブジェクト指向プログラミングの構文は用意されておらず、他の言語と比べて独特なコーディングを強いられてきました。 ところが、Perl v5.38.0 に実験的に class 構文が組み込まれました。 これにより他のプログラミング言語とよく似た形でオブジェクト指向プログラミングができるようになります。 この記事では、Perl v5.38.0 の実験的な class 構文について、Perl ドキュメントの perlclass を参考に紹介します。

目次

最新の Perl のインストール

Perl の class 構文は Perl v5.38.0 以降でないと利用できません。 恐らく多くの環境では Perl v5.38.0 はインストールされていないことでしょう。 また、class 構文はまだ実験的な実装のため、 class 構文を試すためだけにインストール済みの Perl をバージョンアップするのはためらわれます。 そのため、ここでは、ホームディレクトリ内に Perl 本体をインストールして、一時的にそれを使う方法を紹介します。

この記事を書いている時点では、残念ながら Windows 用の Strawberry PerlActive Perl も Perl v5.38.0 は用意されていませんでした。 また、Linux の各種ディストリビューションでも、標準で Perl v5.38.0 は用意されていませんでした。 そのため、ここでは Ubuntu にソースコードから make してインストールします。 一般ユーザー権限で、ホームディレクトリに Perl 本体をインストールします。 CPAN のソースコード配布ページ に掲載されているインストール手順を以下に引用します。

wget https://www.cpan.org/src/5.0/perl-5.38.0.tar.gz
tar -xzf perl-5.38.0.tar.gz
cd perl-5.38.0
./Configure -des -Dprefix=$HOME/localperl
make
make test
make install

make install が終わったら、シェルで次のコマンドを実行します。

export PATH=$HOME/localperl/bin:$PATH

perl を呼び出したときのバージョンを確かめてみます。

$ perl -v

This is perl 5, version 38, subversion 0 (v5.38.0) built for x86_64-linux
[以下省略]

これで、シェルを閉じるまで、perl コマンドで Perl v5.38.0 が呼び出されるはずです。

構文

まずは、class 構文のサンプルコードを見てみましょう

use v5.38;
use feature 'class';

class My::Example 1.234 {
    field $x;
 
    ADJUST {
        $x = "Hello, world";
    }
 
    method print_message {
        say $x;
    }
}
 
My::Example->new->print_message;

このコードを見れば、Perl 経験者でなくても、おおよそ何をやっているのか分かるのではないでしょうか。 My::Example という名前のクラスが定義されています。 そのクラスにはプロパティ xprint_message メソッドが用意されています。 さらに、ADJUST のブロックではプロパティ x が文字列 "Hello, world" で初期化されています。

最後の行では、このクラスのインスタンスを生成して print_message メソッドが実行されています。 当然、この結果として "Hello, world" と出力されるはずです。

なお、現時点では、class 構文を使いたなら、use v5.38;use feature 'class'; の 2 つの宣言は必須です。

実際に上記スクリプトを実行すると、次のような結果が出力されます。

class is experimental at ./test.pl line 6.
field is experimental at ./test.pl line 7.
ADJUST is experimental at ./test.pl line 9.
method is experimental at ./test.pl line 13.
Hello, world

この class 構文はまだ実験的な実装のため、このように警告がいくつも出力されます。

以降、class 構文を詳細に見ていきましょう。

class キーワード | クラスを定義する

クラスの宣言には class キーワードを使います。 class キーワードの後ろに半角スペースを挟んでクラス名を指定します。 次の例は Example という名前のクラスを定義しています。

class Example {...}

次のようにクラス名には名前空間を加えることも可能です。 次の例では My を名前空間として追加しています。

class My::Example {...}

さらに、class 気ワードにはクラスのバージョンも加えることが可能です。 次の例ではバージョン番号 1.000 を指定しています。

class My::Example 1.000 {...}

field キーワード | フィールドを定義する

フィールドとは、クラスの中からしかアクセスできない変数を指します。 このフィールドは、クラスインスタンスごとに記憶領域が用意され、インスタンスごとに独立しています。 オブジェクト指向プログラミングでは、よくメンバー変数とも呼ばれます。

Perl の class 構文では、フィールドはクラス定義の中で field キーワードを使って定義します。

class Member {
    field $userid    = 'Taro';
    field @interests = ( 'car', 'game', 'tech' );
    field %scores    = ( 'A' => 80, 'B' => 40, 'C' => 90 );

    method print_message {
        say join( ', ', $userid, $interests[2], $scores{B} );
    }
}

Member->new->print_message;

上記コードのようにフィールドとしてスカラー変数だけでなく配列や連想配列も定義することができます。 もちろん、HASHREF や ARRAYREF などのリファレンスにすることも可能です。

class Member {
    field $userid    = 'Taro';
    field $interests = [ 'car', 'game', 'tech' ];
    field $scores    = { 'A' => 80, 'B' => 40, 'C' => 90 };

    method print_message {
        say join( ', ', $userid, $interests->[2], $scores->{B} );
    }
}

Member->new->print_message;

ADJUST ブロック | 初期化処理を定義する

ADJUST ブロックを使って、クラスインスタンス生成時の初期化処理を定義することができます。

class Member {
    field $userid = 'Taro';
    field $len;

    ADJUST {
        $len = length $userid;
    }

    method show_userid {
        say "${userid} (${len})";
    }
}

Member->new->show_userid;    # Taro (4)

method キーワード | メソッドを定義する

メソッドは method キーワードを使って定義します。 method キーワードの後ろにはメソッド名を、必要であれば引数を定義することができます。 また、引数のデフォルト値を定義することも可能です。

class Member {
    field $userid = 'Taro';

    method greet( $hi = 'Hi!' ) {
        say $hi . ' ' . $userid;
    }
}

Member->new->greet;             # Hi! Taro
Member->new->greet('Hello');    # Hello Taro

:param 属性 | コンストラクタのパラメーターを受け取る

クラスからインスタンスを生成するとき、つまり、new を呼び出す際に、 コンストラクタにパラメーターを引き渡すことが可能です。

class Member {
    field $userid :param;

    method greet( $hi = 'Hi!' ) {
        say $hi . ' ' . $userid;
    }
}

my $member = Member->new('userid' => 'Taro');
$member->greet; # Hi! Taro

上記の例では、userid という名前に対して "Taro" という値のペアをコンストラクタに引き渡しています。 この引き渡されたパラメータは、:param 属性を使って受け取ります。 パラメータに使われた名前と同じ変数にパラメータ値がセットされることになります。

上記クラス定義において、もし new にパラメータを指定しなかった場合は 例外が投げられます。

# パラメータを指定しなかったために例外が投げられます。
my $member = Member->new();

:param 属性にパラメータの別名を定義することもできます。

class Member {
    field $userid :param(nickname);

    method greet( $hi = 'Hi!' ) {
        say $hi . ' ' . $userid;
    }
}

my $member = Member->new(nickname => 'Taro');
$member->greet; # Hi! Taro

上の例では、本来は userid という名前でパラメータを受け取るところを、 nickname という名前でパラメータを受け取ります。 ただし、別名を定義すると、元の名前 userid ではパラメータを受け取ることはできなくなりますので注意してください。

:isa 属性 | 継承する

:isa 属性 を使って、別のクラスを 1 つだけ継承することができます。

# 親クラス
class Member {
    field $userid :param;

    method greet( $hi = 'Hi!' ) {
        say $hi . ' ' . $userid;
    }
}

# 子クラス
class ProMember :isa(Member) {
    field $nickname :param;

    method thank {
        say 'Thank you, ' . $nickname;
    }
}

my $promember = ProMember->new(userid => 'Taro', nickname => 'Kotaro');
$promember->greet; # Hi! Taro
$promember->thank; # Thank you! Kotaro

上の例では、親クラス Member にはフィールド userid とメソッド greet が定義されています。 子クラス ProMember にはフィールド nickname とメソッド thank が定義されています。 子クラスのインスタンスを生成すると、子クラスに定義されたメソッド thank だけでなく、 親クラスに定義されたメソッド greet も期待通りに呼び出すことができます。

コンストラクタにパラメータが引き渡されていますが、各パラメータの値は親と子のそれぞれに分配されます。 しかし、親クラスに引き渡されたパラメータ値は、子クラスから読み取ることはできません。 つまり、フィールドは継承されません。継承されるのはメソッドのみですので注意してください。

まとめ

以上で Perl v5.38.0 に実験的に実装された class 構文を一通り解説しましたが、 オブジェクト指向プログラミングのすべてを網羅しているわけではありません。 現時点ではまだ実験的実装のため、今後、さまざまな機能が追加されると思われます。 この構文は、ある意味、Perl っぽくないと感じるかもしれませんが、慣れてくると、 この構文のほうが分かりやすく書きやすいと感じることでしょう。 さらなる機能追加を、今後のバージョンアップに期待したいところです。