本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーは、
本稿のサンプルコードは、
なぜ動的な型制約を導入したいのか
動的な型制約とは、
動的な型制約をPerlへ導入する方法の解説に入る前に、
型がないことによる問題
Perlを使うメリットにはたとえば次のものがあります。
- 枯れている言語でドキュメントも多く、
安定して利用できる - CPANモジュールにたくさんの資産があり、
かつそれらのドキュメントは同じような形式で書かれていて扱いやすい - さまざまな環境にデフォルトで実行環境が入っている
- 動的言語でコンパイルがなく、
型を意識する必要がないため、 学習コストが比較的小さく、 すばやく開発できる
しかし、
- 2つの引数を必要としている関数に1つしか引数を渡さなかった
- Blogクラスのオブジェクトを必要としているところに、
別のクラスのオブジェクトを渡した
エラーが起こるまで実行を進めることは、
リスト1は、funcは1つ目の引数に文字列を、func内では最初に$stringを使った処理を、$numberを使った処理を行います。しかし、
sub func {
my ($string, $number) = @_;
// (1)$stringを使った処理
...
// (2)$numberを使った処理
...
}
// (3)引数を1つしか渡していない
func('aiueo');
// (4)2つ目の引数に数字以外を渡す
func('aiueo', 'abcde');
動的な型制約での解決
この問題を解決するにはどうすればよいでしょうか。
一つの方法は使用する言語を静的言語に変えることです。しかし、
言語を変えずにPerlのまま問題を解決する方法として、
リスト1の例なら、func関数は文字列と数字の2つの引数を必要とする」
このように実行時に引数の型をチェックすることによって、
Smart::Argsで引数に型制約を導入する
それでは、
関数の引数に対して動的な型制約をかけるためのCPANモジュールには、
Smart::Argsは自分で定義した関数の最初に簡潔な記法を記述することで、
Smart::Argsは、argsとargs_という2つの記法の使い方と、
Smart::Argsのインストール
Smart::ArgsはCPANモジュールとして提供されているので、cpanmコマンドでインストールします。モジュールをロードできれば、
$ cpanm Smart::Args
$ perl -MSmart::Args -E 'say $Smart::Args::VERSION'Smart::Argsの基本的な使い方
さっそくSmart::Argsを使って、args記法や、args_記法などを紹介します。
args──名前付き引数をチェックする
まずargsという記法を使い、func(number => 1, string => 'abc')のように、numberやstringのような名前を付けて渡すものです。
たとえばfuncという関数があり、numberという名前で整数を、stringという名前で文字列を受け取りたいとします。そのように型制約を付けたい場合、args記法で制約を書きます。
use Smart::Args;
sub func {
args my $number => 'Int',
my $string => 'Str';
print(sprintf('%d:%s', $number, $string));
}定義したfunc関数を正しい引数で呼び出すと、numberという名前で渡した引数は$numberに、stringという名前で渡した引数は$stringに/代入され、
func(number => 10, string => 'aiueo');
# 10:aiueo と出力されるもし関数の使い方を間違えて、func関数のnumberという引数に、
func(number => 1.5, string => 'aiueo');この場合、'number': Validation failed for 'Int'with value 1.のように、numberという引数はIntを期待していたが1.func関数を実行せずに終了します。
Smart::Argsで型制約をかけた場合、stringという名前の引数を渡し忘れた例です。
func(number => 10);この場合も、missing mandatory parameter named'$string'のように、stringという引数は必須だが渡されていない」func関数を実行せずに終了します。
args記法に渡せる型には、IntやStrなどさまざまなものがあります。指定できる型について詳しくは、
default──引数を渡さない場合のデフォルト値を設定する
先ほど、
オプションを利用したい場合は、Intなどの文字列を渡していた部分にハッシュを渡します。型はisaに記述し、defaultに記述します。
次のコードは、pという引数を渡さなかった場合、
use Smart::Args;
sub func_with_default {
args my $p => { isa => 'Int', default => 10 };
print($p);
}
func_with_default(p => 5);
# 5と出力される
func_with_default();
# 10と出力されるクラスメソッド、インスタンスメソッドに型制約を付ける
今までは関数に型制約をかけてきましたが、
リスト2は、Fooパッケージにクラスメソッドとインスタンスメソッドを定義した例です。メソッドに型制約を導入する場合は、$classもしくは$selfという変数名を使って定義します。このようにすることで、$classや$selfの変数に、ClassNameを使えます。
package Foo;
use Smart::Args;
sub new {
my ($class) = @_;
return bless {}, $class;
}
sub class_method {
args my $class => 'ClassName', ━(1)
my $p => 'Int';
}
sub instance_method {
args my $self, ━(2)
my $q => 'Str';
}
Foo->class_method(p => 3);
my $foo = Foo->new;
$foo->instance_method(q => 'abc');
args_pos──通常の引数をチェックする
argsでは名前付き引数を取り扱いました。しかし名前付き引数ではなく、func(3,'abc')のように通常の引数として受け取りたい場合もあります。このときはargs_を利用します。記法の名称が変わっただけで、argsと同じです。
use Smart::Args;
sub func_with_args_pos {
args_pos my $p => 'Int',
my $q => { isa => 'Str', default => 'aiu' };
print(sprintf('%d:%s', $p, $q));
}
func_with_args_pos(3, 'abc');
# 3:abcと出力される制約として指定できる型
ここでは、
Smart::Argsは内部でCPANモジュールのMouseの型のしくみを利用しており、
- Mouseからデフォルトで提供されている型
- クラス名
- Mouse::Util::TypeConstraintsを利用して、
自分で独自に定義した型
Mouseからデフォルトで提供されている代表的な型には、
| 型名 | 意味 |
|---|---|
| Bool | 真偽値を受け付ける型 |
| Int | 整数を受け付ける型 |
| Num | 小数も含めた数字を受け付ける型 |
| Str | 文字列を受け付ける型。ただし、 |
| ArrayRef[type] | 配列リファレンスを受け付ける型。typeに型を指定すると、 |
| HashRef[type] | ハシュリファレンスを受け付ける型。typeに型を指定すると、 |
| Maybe[type] | typeとして指定した型、 |
クラス名を型として指定すると、uriという名前でURIクラスのインスタンスを、fooという名前でFooクラスのインスタンスを受け取る関数を定義する例です。
use URI;
use Smart::Args;
sub args_with_uri_and_foo {
args my $uri => 'URI',
my $foo => 'Foo';
}
my $uri = URI->new('http://example.com/');
my $foo = Foo->new;
args_with_uri_and_foo(uri => $uri, foo => $foo);ほかにもMouse::Util::TypeConstraintsというモジュールを利用すれば、
ここまでで紹介したさまざまな型を利用すれば、undefという型を定義できます。
sub args_with_uri_array {
args my $uris => 'Maybe[ArrayRef[URI]]';
}
my $uri1 = URI->new;
my $uri2 = URI->new;
# 呼び出しは両方成功する
args_with_uri_array(uris => [ $uri1, $uri2 ]);
args_with_uri_array(uris => undef);<続きの
本誌最新号をチェック!
WEB+DB PRESS Vol.130
2022年8月24日発売
B5判/
定価1,628円
ISBN978-4-297-13000-8
- 特集1
イミュータブルデータモデルで始める
実践データモデリング
業務の複雑さをシンプルに表現! - 特集2
いまはじめるFlutter
iOS/Android両対応アプリを開発してみよう - 特集3
作って学ぶWeb3
ブロックチェーン、スマートコントラクト、 NFT


