Perl Hackers Hub

第79回最近Perlに追加された実験的機能
try文⁠defer文⁠class文(2)

本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーはcharsbarこと石垣憲一さんで、テーマは「最近Perlに追加された実験的機能」です。

<前回(1)こちら。>

今後実装されそうな機能

実際にPerl本体に入った機能と、プロトタイピングに使われたCPANモジュールの機能やToDoメモを比較すると、これから実装されていくであろう機能が予想できます。同様に、Paul Evans氏がSyntax::KeywordSyntax::Operatorといった名前を冠してリリースしているほかの実験的なモジュールも、今後取り込みの対象となっていくことが予想されます。ここではその一部を紹介します。

複数のcatchブロック ─⁠─より柔軟なエラー処理

Syntax::Keyword::Tryモジュールにはバージョン0.15(2020年)以降、エラーの種類に応じた複数のcatchブロックを持つ構文が実験的な機能として存在しています。ここではPerl 5.36で正式な機能に昇格したisa演算子を利用して、tryブロックの中で発生したエラーが既知のMyExceptionクラスのものか、それ以外のエラーなのかを判定しています。

use v5.36;
use Exception::Class 'MyException';
use Syntax::Keyword::Try qw(
    try
    :experimental
);

try {
    do_something()
        or die MyException->throw(
           error => 'exception',
        );
}
catch ($e isa MyException) { ... }
catch ($e) { ... }

さまざまな演算子

Perl 5.10(2007年)で導入されたスマートマッチ演算子はさまざまな問題が発覚して、Perl 5.18(2013年)で実験的機能に格下げとなっていましたが、Perl 5.37.10(2023年)でついに廃止となり、2025年にリリースされるであろうPerl 5.42で削除が予定されています。

その反省もあって、Perl本体のほうでは演算子もキーワードと同じようにプロトタイピングできるようにするしくみの実験が続いていたのですが、Perl 5.37.7でそのしくみが取り込まれたので、2021年から用意されていた独自演算子を追加するモジュールの実験成果も順次取り込まれていくことが予想されます。

Syntax::Operator::Equモジュール

従来のコードでは、未定義値が入るかもしれない変数を適切に比較するには次のように書く必要がありました。

if ((defined $x && defined $y && $x eq $y) ||
    (!defined $x && !defined $y)) { ... }
if ((defined $x && defined $y && $x == $y) ||
    (!defined $x && !defined $y)) { ... }

Syntax::Operator::Equモジュールは、従来の等価演算子の動作に加えて両方とも未定義の場合も真を返す演算子を提供します。

use v5.37;
use Syntax::Operator::Equ;

if ($x equ $y) { ... }
if ($i === $j) { ... }

Syntax::Operator::ExistsOrモジュール

Perl 5.10では値が定義されていなければ右辺の値を返す//演算子が導入されましたが、Syntax::Operator::ExistsOrモジュールはハッシュにキーが存在していなければ右辺の値を返す\\演算子を提供します。

use v5.37;
use Syntax::Operator::ExistsOr;

my %hash = (foo => 'bar');
say $hash{baz} \\ 'quuz'; # quuz

Syntax::Operator::Dividesモジュール

Syntax::Operator::Dividesモジュールは、倍数かどうかを判定する%%演算子を提供します。

use v5.37;
use Syntax::Operator::Divides;

for (1 .. 100) {
    if ($_ %% 15) { say "FizzBuzz"; }
    elsif ($_ %% 5) { say "Fizz"; }
    elsif ($_ %% 3) { say "Buzz"; }
    else { say $_; }
}

matchcase文 ─⁠─条件分岐の再実装

複数の条件分岐を1つにまとめるgivenキーワード、whenキーワードもスマートマッチ演算子とともに廃止が決まり、削除が予定されています。その代替として、Syntax::Keyword::Matchモジュールの実験成果も取り込まれていくことでしょう。match文は、従来のgiven文と異なり比較に使う演算子を明示的に指定するのが特徴です。

use v5.37;
use Syntax::Keyword::Match;
use Syntax::Operator::Equ;

match ($str : equ) {
    case (undef) {
        say "not defined"
    }
    case ("") {
        say "defined but empty"
    }
    default {
        say "non-empty"
    }
}

クラスの各種機能

Perl 5.37.9では本当に最小限の機能しか実装されませんでしたが、クラスの実装とともに新たに追加されたperlclassドキュメントには、優先的な実装が期待されるToDoがいくつか記載されています。

ロール ─⁠─再利用可能なメソッド群

Object::Padモジュールには、再利用可能なメソッドをまとめたロールを定義するroleキーワードが用意されています。ロールをクラスに組み込むにはclass文の:does属性を利用します。継承の順序に応じてメソッドが上書きされていく多重継承と異なり、ロールの組込み時にはメソッドの衝突が確認されるので、うっかりメソッドが上書きされることはありません。

use Object::Pad;

role Greet {
    method say_hi {
        say "Hi, I'm " . $self->name;
    }
}

class Person :does(Greet) {
    ...
    # method say_hi { say "Hi, I'm $name"; }
}

Object::Padモジュールの:does属性は必要に応じて複数個設定できます。1つの:does属性の中に複数個のロールを設定することはできません。

field文のアクセサ属性 ─⁠─メソッドの自動生成

Object::Padモジュールには、field文に:reader:writer:accessorという属性を追加することでアクセサメソッドを自動的に生成するしくみが用意されています。

use Object::Pad;

class Person {
    field $name :param :reader;
    field $age :param :accessor;
    # method name { $name }
    # method age {
    # @_ ? $age = shift : $age;
    # }
}

メタプログラミング

クラスの内部をのぞいたりクラスの機能を拡張したりするのに必要なメタプログラミングの機能も課題に挙がっています。メタプログラミング機能は、Object::Padモジュールのバージョン0.78時点でもまだ実験的な機能の扱いです。

use v5.36;
use Object::Pad qw(:experimental);
use Object::Pad::MetaFunctions qw(metaclass);

class Author :isa(Person) :does(Greet) { ... }

my $author = Author->new(...);

my $meta = metaclass($author);
say $_->name for $meta->superclasses;
say $_->name for $meta->all_roles;
say $_->name for $meta->direct_methods;

メタプログラミング機能については、Perlの機能変更の提案を管理しているGitHubリポジトリで、正式な機能提案に向けての議論が進んでいます。このリポジトリも最近のPerlの開発体制の変化とともに導入されたもので、もとはRFCsRequests For Commentsと呼ばれていたのが最近PPCsProposed Perl Changesと名称が変わりました。本稿でも取り上げたdeferキーワードのほか、Perl 5.36で導入されたbuiltin名前空間や、Perl 5.38で導入が予定されているexport_lexicallyキーワードなどの議論もまとめられています。

周辺ツールの対応

クラスは新しいしくみですので、Perl本体だけでなく、周辺ツールの対応も必要になります。

たとえば、class文によるオブジェクトの内部構造は従来のbless文によるオブジェクトとは異なるため、本稿執筆時点ではData::Dumperモジュールで内部を見ようとしたり、Storableモジュールで複製を作成しようとしたりすると、エラーが発生します。

また、モジュールのバージョンを抽出するツールなどもclass文には未対応のものが多いため、そのままではCPANへのアップロードなどの際に問題が発生します。この問題については次の例のように従来のpackage文と$VERSION変数を利用したバージョン指定を併記しておくことで回避できますが、いずれはツールチェイン側の対応が入るはずです。

use v5.37;
use experimental qw(class);

package Author;
our $VERSION = '0.01';

class Author 0.01 { ... }

まとめ

今回紹介した機能はいずれもまだ実験が続いているため、正式な機能に昇格するまでには時間がかかりそうですが、プロトタイピングは済んでいるため大きく変化することはないでしょう。新たにPerlのコードを書くことがあったらぜひ積極的に使ってみてください。これらの機能がそろった暁には、Perlのメジャーバージョンが(ほぼ30年ぶりに)上がることになるかもしれません。

さて、次回の執筆者は宮川達彦さんで、テーマは「CarmelによるPerlモジュール依存管理」です。お楽しみに。

おすすめ記事

記事・ニュース一覧