Perl Hackers Hub

第52回 Perlで堅牢な開発―構文チェック,静的検査,型制約(2)

この記事を読むのに必要な時間:およそ 4 分

前回の(1)こちらから。

Type::Tinyを用いた型制約

型制約とは期待している値の範囲を表し,たとえばNumという型制約であれば数値全体を表します。できてよいことだけできるのが,良い型制約です。

ここでは,Perlの型制約のType::Tiny 1.004002を紹介します。

Type::Tiny ─⁠─ Perlのモダンな型制約

Type::Tinyは型制約とその周辺ツールを同封したモジュールで,TOBYINKによって作られました。WebアプリケーションフレームワークのDancer2GraphQLの型制約として利用されています。

Type::Tinyには,大きく3つの特徴があります。

  • ポータビリティが高い
    • MooseMouseMooなどのクラスビルダの型制約と互換性がある
    • 基本コアモジュールのみに依存している
  • 定義済み型制約ライブラリの使い勝手が良い
  • パフォーマンスが十分良い
    • ピュアPerl実装のType::Tinyでも,Sub::Quote::quote_subで型の判定をインライン展開し,速い
    • Type::Tiny::XSRef::Util::XSなどXSモジュールで必要に応じてスピードアップできる

まずは,Type::Tinyを直接使ってみましょう。次のコードは偶数の型制約を作っています。偶数判定するコードリファレンスを渡し,$_は判定したい値です。

use Type::Tiny;

my $even = Type::Tiny->new(
    constraint => sub { $_ % 2 == 0 },
);
$even->check(3); # => not ok
$even->check(4); # => ok

Type::Tiny表2の判定メソッドを持ち,Moose(あるいはMouse)::Meta::TypeConstraintと似た使い勝手になっています。

表2 型制約の判定関連メソッド

メソッド説明
check($value) : Bool型制約を満たすか否か真偽値を返す
validdate($value) : Maybe(Str)型制約を満たさなければエラーメッセージを返す
assert_valid($value) : 1 or die型制約を満たさなければdie
get_message($value) : Str与えられた値に対するエラーメッセージ。型制約を満たすか否かは問わない

Type::Libraryで,型制約を再利用可能にする

Type::Tinyと同時にインストールされるモジュールに,Type::LibraryType::UtilsTypes::Standardがあります。Type::Libraryを利用すれば,型制約の再利用が可能になります。Type::Utilsdeclareaswhereenumといった型制約定義のためのDSLDomain Specific Languageドメイン特化言語)を提供します。そしてTypes::Standardは,後述の通りIntStrといった基本的な型制約を提供します。これらのモジュールを利用することで,簡潔に型制約を宣言できます。

次のコードは,偶数と血液型の型制約を定義しています。

package MyType;
use strict;
use warnings;

use Type::Library -base;
use Type::Utils;
use Types::Standard -types;

# 偶数
declare 'Even',
    as Int,
    where { $_ % 2 == 0 };

# 血液型
enum 'Blood', ['A', 'B', 'AB', 'O'];

1;

型制約EvenBloodは宣言した名前でエクスポートでき,スッキリと記述できます。

use Test::More;
use MyType qw(Even Blood);

ok not Even->check(3);
ok Even->check(4);

ok Blood->check('A');
ok not Blood->check('C');

done_testing;

エクスポートオプションはほかにも豊富にあるので,Type::Libraryのドキュメントを参照してください。

DSLを使わずType::Libraryを利用する

理解のために,DSLを使わず上述の型制約と等価のパッケージを用意してみます。

package MyTypeWithoutDSL;
use strict;
use warnings;

use Type::Library -base;
use Type::Tiny;
use Types::Standard qw(Int Str);

__PACKAGE__->meta->add_type(
    Type::Tiny->new(
        name       => 'Even',
        parent     => Int,
        constraint => sub { $_ % 2 == 0 }
    )
);

__PACKAGE__->meta->add_type(
    Type::Tiny->new(
        name       => 'Blood',
        parent     => Str,
        constraint => sub { m!\A(?:A|B|AB|O)\z! }
    )
);

__PACKAGE__->meta->make_immutable;

Type::Libraryは,__PACKAGE__->metaに型制約の情報を保存し,Type::Utilsはこのメタオブジェクトを隠蔽いんぺいするDSLを提供していることがわかります。たとえば,このメタオブジェクトを利用して,__PACKAGE__->type_namesで定義した型制約の一覧を取り出すことができます。

著者プロフィール

小林謙太(こばやしけんた)

東北大学大学院数学専攻修了後,2011年に株式会社モバイルファクトリーに新卒入社。

ソーシャルゲームの開発やプロダクトマネジメントに従事したのち,現在は,育成・採用・広報などのエンジニア組織の課題解決に取り組む。また,Japan Perl Associationのメンバーとして,YAPC::JapanやGotanda.pmなどのコミュニティを中心に活動している。

コメント

コメントの記入