***************************************************** * GENERATED FILE, DO NOT EDIT * * THIS IS NO SOURCE FILE, BUT RESULT OF COMPILATION * ***************************************************** This file was generated by po4a(7). Do not store it (in VCS, for example), but store the PO file used as source file by po4a-translate. In fact, consider this as a binary, and the PO file as a regular .c file: If the PO get lost, keeping this translation up-to-date will be harder. =encoding UTF-8 =pod =head1 NAME Moose::Manual::Types - Moose 的类型系统 =head1 VERSION version 2.0401 =head1 和 Perl 的类型相似? Moose 拥有自己的类型系统。你也可以配合 MooseX 模块用来进行参数验证。 Moose 的类型系统是根据 Perl 5 的默认类型以及 Perl 6 的一些新概念而来的。当然,你也可以自己定义新的类型。 每个类型都有一个名字,你可以通过命名空间重复使用它们,所以很容易的在大型应用程序中共享类型。 然而,这不是一个"实际"的类型系统。Moose 不会奇迹般的使 Perl 关联类型的变量,这只是一个比较先进的类型检查系统,可以让你的参数通过约束的名字来进行检查。 尽管那样说,Moose 类型系统仍然是非常有用的,而且它是我们认为 Moose 强大的原因之一。好好的使用 Moose 类型系统,可以方便我们得到有效的数据,以及更好的维护我们的程序。 =head1 Moose 类型 基本的 Moose 类型层次结构看起来时这样的。 Any Item Bool Maybe[`a] Undef Defined Value Str Num Int ClassName RoleName Ref ScalarRef[`a] ArrayRef[`a] HashRef[`a] CodeRef RegexpRef GlobRef FileHandle Object 在实践中,C 和 C 是有概念性的区别的。C 是层次结构中的顶层类型。 这些类型的其余部分对应于现有的 Perl 的概念。特别是: =over 4 =item C 接受 C<1> 为 true, 接受 undef, 0, 或者空字符串为 false。 =item C 接受 C<`a> 或 C。 =item C 接受任何 perl 认为的数字。(详见 L) =item C 和 C 接受字符串(类名或角色名)。当约束被检查时,类或角色必须已经被载入。 =item C 接受 L 或者 Perl 内建句柄。(详见 L) =item C 接受被绑定的引用。 =back "[`a]" 的类型可以被参数化。因此,不是只有简单的 C,也可以使用C,甚至是 C。 C 需要特别说明下。当你只是单独使用它的时候,它没有任何意义。但当被参数化后,就表明它的值是 C 或者是参数化的类型。所以,C 表示整数或者 C。 想要了解更多的类型结构的细节,请查阅 L。 =head1 什么是类型? 你需要意识到类型不是一个类(或包)。类型只是对象名字和属性约束(准确的讲是L)。Moose 会维护一个全局的注册过的类型名字。对象可以转换成相应的名称,如 C。 然而, 类名I<可以>是类型名称。当你使用 Moose 定义一个新的类时,它会自动关联的定义一个相关的类型名称: package MyApp::User; use Moose; 现在你就可以用 C<'MyApp::User'> 做为一个类型名称: has creator => ( is => 'ro', isa => 'MyApp::User', ); 但是,非 Moose 类是不能这样的。这时你需要显示的声明类型信息。 has 'birth_date' => ( is => 'ro', isa => 'DateTime', ); 一般情况下,当 Moose 见到一个未知的名字时,都会假设这个名字是一个类名。 subtype 'ModernDateTime' => as 'DateTime' => where { $_->year() >= 1980 } => message { 'The date you provided is not modern enough' }; has 'valid_dates' => ( is => 'ro', isa => 'ArrayRef[DateTime]', ); 在上面这两个实例中,Moose 都会假设这个 C 是一个类名。 =head1 子类型 Moose 在其内建的层次结构中也有子类型。如,C 就是 C 的子类型。 子类型是在父类型和约束下的。子类型的值必须先通过父类型的约束检验,然后再通过自己的约束检验,才是有效的值。 也就是说,子类型会通过父类型的约束和自己的约束来达到更加具体的约束。 子类型也可以定义自己约束检测失败时的消息。比如你可能会见到如下出错信息"你提供的值为20,它不是一个有效的值,有效的值必须是1-10"。这种错误信息比默认定义的更加友好。还有一个更加友好的方法,你可以通过 L 来得到更加友好提示信息。 这里有一个简单但是有用的例子: subtype 'PositiveInt', as 'Int', where { $_ > 0 }, message { "The number you provided, $_, was not a positive number" }; 注意,所有关于类型的语法糖在 L 中被提供。 =head1 类型名称 类型名称在 Perl 的环境中是全局存在的。Moose 会把注册的名称映射到类型对象中。有关注册过程,请见 L。 如果你在同一个进程中有多个 apps 或者函数库使用 Moose,可能会有一些名字冲突。所以我们推荐你在你的类型名字前加前缀。 如,定义类型名称为"MyApp::Type::PositiveInt",而不是"PositiveInt"。我们建议你集中定义所有的类型在 C 中,这样也方便于移植。 不管怎样,在你那样做之前,我们建议你去看看 L 模块。这个模块可以帮助你创建一个"类型库",这个库可以很方便的导入到你的程序中。 has 'counter' => (is => 'rw', isa => PositiveInt); 这使得你可以使用一个简短的名称而不需要到处都使用完全限定名。它还允许你轻松的创建参数类型: has 'counts' => (is => 'ro', isa => HashRef[PositiveInt]); 这个模块会在编译时进行检查,而且它通常在解析比较复杂的字符串类型时更加强壮。 =head1 强制类型转化 强制类型转化可以让 Moose 帮你将一个类型转换成另外一个类型。 subtype 'ArrayRefOfInts', as 'ArrayRef[Int]'; coerce 'ArrayRefOfInts', from 'Int', via { [ $_ ] }; 你会发现我们并没有在 C 的基础上进行强制转化,这是因为 Moose 不推荐在原生类型上进行强制转化。 强制类型转换是全局的,就像类型名称,所以一个内建类型被强制转化后,整个程序都会使用转化后的类型。这就是上面提到的另一个使用良好命名空间的原因。 Moose I<绝对不会>自己对类型进行强制转化,除非你通过设置 C 选项为 true来明确要求。 package Foo; has 'sizes' => ( is => 'ro', isa => 'ArrayRefOfInts', coerce => 1, ); Foo->new( sizes => 42 ); 此代码示例是正确的,创建的新对象将有C<[ 42 ]>作为其 C 的属性。 =head2 递归转化 递归转化是转化类型参数的类型参数。让我们看下面这个例子: subtype 'HexNum', as 'Str', where { /[a-f0-9]/i }; coerce 'Int', from 'HexNum', via { hex $_ }; has 'sizes' => ( is => 'ro', isa => 'ArrayRef[Int]', coerce => 1, ); 如果我们传递十六进制数组的引用给 C,Moose 不会为我们转化。 然而,你可以通过定义一个子类型使其在两个参数类型间进行转化。 subtype 'ArrayRefOfHexNums', as 'ArrayRef[HexNum]'; subtype 'ArrayRefOfInts', as 'ArrayRef[Int]'; coerce 'ArrayRefOfInts', from 'ArrayRefOfHexNums', via { [ map { hex } @{$_} ] }; Foo->new( sizes => [ 'a1', 'ff', '22' ] ); 现在 Moose 将会强制转化十六进制数为整数。 Moose 不会尝试级联的强制转化,所以它不会强制转化单一的十六进制数。所以要做到这一点,我们需要定义一个单独的强制类型转化: coerce 'ArrayRefOfInts', from 'HexNum', via { [ hex $_ ] }; 虽然这样写很冗长,但是在这个强制类型转化的魔法中,我们认为这样是最好的,因为这样写会非常清楚。 =head1 类型联合 Moose 允许你声明一个属性为一个或一个以上不同的类型。比如下面这个例子: has 'output' => ( is => 'rw', isa => 'Object | FileHandle', ); Moose 会解析这个字符串,并认出你正在创建的类型是什么。C 属性会接受任何类型的对象或未绑定的句柄。 当你使用类型联合的时候,你应该仔细考虑是否强制转化是个更好的解决方法。 对于我们上面的例子,使用对象的 C 方法是个更好的解决方法。 duck_type 'CanPrint', [qw(print)]; 我们可以转化句柄到一个对象,只需要简单的封装这个类: package FHWrapper; use Moose; has 'handle' => ( is => 'rw', isa => 'FileHandle', ); sub print { my $self = shift; my $fh = $self->handle(); print {$fh} @_; } 现在我们能定义一个从 C 到我们封装的类的强制转化: coerce 'CanPrint' => from 'FileHandle' => via { FHWrapper->new( handle => $_ ) }; has 'output' => ( is => 'rw', isa => 'CanPrint', coerce => 1, ); 这种模式使用一个类型转化,而不是一个类型联合,这将使你的类内部更加简单些。 =head1 类型创建助手 L 中包含创建指定类型的一些助手函数,包括C,C,C 和 C 等等。具体的请查阅文档。 一个比较有用的就是 C 类型,它允许你创建一个有 C 的子类型,让你指定它可能出现的值。 enum 'RGB', [qw( red green blue )]; 这样就创建了一个叫做 C 的类型。 =head1 匿名类型 创建类型的函数均返回类型的对象。这个对象可以被用在任何可以使用的地方。 has 'size' => ( is => 'ro', isa => subtype( 'Int' => where { $_ > 0 } ), ); 这对于你只想使用一次的类型,是非常方便的,你不需要纠结于给它起一个什么样的名字。 =head1 确认方法的参数 Moose 不提供任何验证方法的参数手段。不过,也有几个 CPAN 上的拓展可以让你这么做。 最简单的就是使用 L 模块,这个可以让你通过Moose 的类型来对你的参数进行检查。 use Moose; use MooseX::Params::Validate; sub foo { my $self = shift; my %params = validate_hash( \@_, bar => { isa => 'Str', default => 'Moose' }, ); ... } L 也支持类型强制转化。 也有一些更强大的拓展来支持参数的验证,只要加载 L模块,这个模块会给我们提供相关的关键字的。 method morning ( Str $name ) { $self->say("Good morning ${name}!"); } =head1 加载顺序问题 因为 Moose 的类型是在运行时声明的,所以你可能会遇到一些加载顺序问题。特别是,你可能使用一个类的类型约束,在此之前这个类型已经定义过了。 为了改善这个问题,我们推荐你定义你I<所有>的类型在一个模块中,然后在其他的模块中加载这个模块。 =head1 作者 Moose 是由许多志愿者共同努力的结果。具体的请参看 L 和L译者:xiaomo(wxm4ever@gmail.com) =head1 版权和许可 This software is copyright (c) 2011 by Infinity Interactive, Inc.. 这是自由软件,您可以重新分配或者根据 Perl 5 的编程语言系统本身相关的条款进行修改。