前回の(1)はこちら から。
Mouse::Util::TypeConstraintsで独自の型を定義する
(1)で、Mouse::Util::TypeConstraintsを利用して独自に型を定義できると紹介しました。( 2)でその方法について解説します。
Mouse::Util::TypeConstraintsは、自分で独自に型を定義するユーティリティを提供してくれるモジュールです。このモジュールはMouseというディストリビューションに含まれているため、Mouseをcpanm
でインストールすることで利用できます。cpanm
コマンドでインストールし、正しくロードできればインストールは成功しています。
$ cpanm Mouse
$ perl -MMouse -E 'say $Mouse::VERSION'
独自の型を定義するユーティリティはいくつもあるため、その中で代表的なものを紹介します。そのほかの方法は、Mouse::Util::TypeConstraintsのドキュメント を参照してください。
subtypeを使った既存の型の拡張
subtype
を利用すれば、デフォルトで定義されている型や独自で定義した型から派生し、拡張できます。subtype
はDSL(Domain Specific Language 、ドメイン特化言語)のように利用できます。渡せるオプションは次のとおりです。
as
どの型から派生するか
where
その型が満たすべき制約。Perlのコードであればどんなものでも書ける。書いたコードが真を返せば制約を満たし、偽を返せば制約を満たさない型を定義できる
message
制約を満たさなかった場合のエラーメッセージ
次のコードは、正の整数であるNatural
型を定義するコードです。
use Mouse::Util::TypeConstraints;
use Smart::Args;
// 型定義
subtype 'Natural'
=> as 'Int'
=> where { $_ > 0 }
=> message { "This number($_) is not Natural" };
sub func {
args my $p => 'Natural';
}
func(p => 3);
func(p => -1);
func(p => 'aiueo');
(1) のようにsubtype
を用いて型を定義します。where
とmessage
の中では$_という変数が利用でき、中には型を利用した関数の引数に渡された値が代入されています。(2) の場合は引数が正の整数なので正常に実行できます。(3) の場合は引数が負の整数なので、message
で定義した'p': This number(-1) is not Natural
が出力され、エラーで終了します。(4) ではそもそもNatural
が継承しているInt
型ですらないので、この場合もエラーとなります。
Natural
型からさらに派生し、5未満の正の整数という型NaturalLessThanFive
の定義もできます。
use Mouse::Util::TypeConstraints;
subtype 'NaturalLessThanFive'
=> as 'Natural'
=> where { $_ < 5 }
=> message { "$_ is not less than five" };
where
にはPerlのコードならどんなものでも書けるので、メールアドレスとして妥当である型EmailAddress
を定義できます。CPANで公開されているEmail::Valid::Looseモジュールを使って次のように書きます。
use Mouse::Util::TypeConstraints;
use Email::Valid::Loose;
subtype 'EmailAddress',
=> as 'Str'
=> where {
Email::Valid::Loose->address(-address => $_)
}
=> message { "$_ is not a valid email address" };
enumを使った型の定義
関数の引数に対して、列挙した文字列のどれかしか受け付けないように制約をかけたい場合があります。enumを利用すれば、そのような制約をかけるための型を定義できます。
次のコードは、色として赤(red
) ・青(blue
) ・緑(green
)のどれかしか受け付けない型を定義する例です。
use Smart::Args;
use Mouse::Util::TypeConstraints;
enum 'RGBColor' => ['red', 'blue', 'green'];
sub func {
args my $p => 'RGBColor';
}
func(p => 'red'); # ok
func(p => 'black'); # error
duck_typeを使った型の定義
duck_type
を利用すると、指定したメソッドを持つオブジェクトのみを受け入れる型を定義できます。
次のコードは、id
メソッドとas_string
メソッドの両方を持つクラスのオブジェクトを受け入れる型を定義する例です。
use Mouse::Util::TypeConstraints;
duck_type 'HasIdAndAsStringMethod' => [qw(id as_string)];
型を定義するときの注意点
独自の型を定義する際、一つ注意点があります。それは、定義する型はグローバルな名前空間に作られることです。別々のパッケージで型を定義したとしても、名前が同じなら両方を読み込んだ際にコンフリクトし、あとから読み込まれたほうに上書きされます。
ドキュメントによると、特定のパッケージで型を定義する場合、パッケージ名を含めた型名にすることが推奨されています。たとえばBlog
パッケージでブログが取り得る状態を型として定義するなら、次のようにBlog::Status
という名前で定義します。
package Blog;
use Mouse::Util::TypeConstraints;
enum 'Blog::Status' => [qw(public private)];
<続きの(3)はこちら 。>
特集1
イミュータブルデータモデルで始める
実践データモデリング
業務の複雑さをシンプルに表現!
特集2
いまはじめるFlutter
iOS/Android両対応アプリを開発してみよう
特集3
作って学ぶWeb3
ブロックチェーン、スマートコントラクト、NFT