如何更改属性类型? (珀尔哞)
How to change attribute types? (Perl Moo)
我们使用 Perl Moo。
让我们定义一组属性:
package C;
use Moo;
use Types::Standard qw(Str Int Num Maybe);
has 'x' => (is=>'rw', isa=>Str);
has 'y' => (is=>'rw', isa=>Int);
has 'z' => (is=>'rw', isa=>Int);
# here to insert make_optional() described below
1;
我想编写一个例程,将某些属性的 T 替换为 Maybe[T]。例如:make_optional(qw(x y))
应该使类型 x
Maybe[Str]
和类型 y
Maybe[Int]
.
如何用 Moo 做?
你不能。
Moo does not have a Meta Object Protocol。没有它,您将无法返回并更改内容。
There is no meta object. If you need this level of complexity you need Moose - Moo is small because it explicitly does not provide a metaprotocol.
此外,类型只是代码参考。
There is no built-in type system. isa is verified with a coderef; if you need complex types, Type::Tiny can provide types, type libraries, and will work seamlessly with both Moo and Moose.
你可以做的是编写一个类型来访问其他地方的某种单例,以确定它的行为是否像 Maybe[Str]
或 Str
,但这是一个远景,可能丑陋和疯狂你不应该这样做。
[[请注意 Maybe
类型并没有真正使属性可选 per-se,而是 undef-tolerant。默认情况下,Moo 属性已经是可选的。但为了便于讨论,我将继续使用可选与必需的术语。]]
因为我不喜欢“你不能”的答案,这里有一些代码可以满足你的需求...
use strict;
use warnings;
BEGIN {
package MooX::MakeOptional;
use Types::Standard qw( Maybe Any );
use Exporter::Shiny our @EXPORT = qw( make_optional has );
use namespace::clean;
sub _exporter_validate_opts {
my $opts = pop;
$opts->{orig_has} = do {
no strict 'refs';
\&{ $opts->{into} . '::has' };
};
$opts->{attributes} = [];
'namespace::clean'->clean_subroutines( $opts->{into}, 'has' );
}
sub _generate_has {
my $opts = pop;
my $attributes = $opts->{attributes};
return sub {
my ( $name, %spec ) = @_;
if ( ref($name) eq 'ARRAY' ) {
push @$attributes, $_, { %spec } for @$name;
}
else {
push @$attributes, $name, \%spec;
}
};
}
sub _generate_make_optional {
my $opts = pop;
my $attributes = $opts->{attributes};
my $orig_has = $opts->{orig_has};
return sub {
my %optional;
$optional{$_} = 1 for @_;
while ( @$attributes ) {
my ( $name, $spec ) = splice( @$attributes, 0, 2 );
if ( $optional{$name} ) {
$spec->{isa} = Maybe[ $spec->{isa} or Any ];
}
$orig_has->( $name, %$spec );
}
}
}
}
{
package C;
use Moo;
use MooX::MakeOptional;
use Types::Standard qw( Str Int );
has 'x' => ( is => 'rw', isa => Str );
has 'y' => ( is => 'rw', isa => Int );
has 'z' => ( is => 'rw', isa => Int );
make_optional( qw(x y) );
}
它所做的是用一个虚拟替换替换 Moo 的 has
关键字,它除了将属性定义存储到数组中外什么都不做。
然后当 make_optional
被调用时,它遍历数组,并将每个属性定义传递给 Moo 的原始 has
关键字,但如果指定则更改为可选。
类 use MooX::MakeOptional
总是 需要确保他们在 class 定义的末尾调用 make_optional
函数, 即使它们没有可选属性。如果他们没有可选属性,他们应该只调用 make_optional
并传递一个空列表。
我们使用 Perl Moo。
让我们定义一组属性:
package C;
use Moo;
use Types::Standard qw(Str Int Num Maybe);
has 'x' => (is=>'rw', isa=>Str);
has 'y' => (is=>'rw', isa=>Int);
has 'z' => (is=>'rw', isa=>Int);
# here to insert make_optional() described below
1;
我想编写一个例程,将某些属性的 T 替换为 Maybe[T]。例如:make_optional(qw(x y))
应该使类型 x
Maybe[Str]
和类型 y
Maybe[Int]
.
如何用 Moo 做?
你不能。
Moo does not have a Meta Object Protocol。没有它,您将无法返回并更改内容。
There is no meta object. If you need this level of complexity you need Moose - Moo is small because it explicitly does not provide a metaprotocol.
此外,类型只是代码参考。
There is no built-in type system. isa is verified with a coderef; if you need complex types, Type::Tiny can provide types, type libraries, and will work seamlessly with both Moo and Moose.
你可以做的是编写一个类型来访问其他地方的某种单例,以确定它的行为是否像 Maybe[Str]
或 Str
,但这是一个远景,可能丑陋和疯狂你不应该这样做。
[[请注意 Maybe
类型并没有真正使属性可选 per-se,而是 undef-tolerant。默认情况下,Moo 属性已经是可选的。但为了便于讨论,我将继续使用可选与必需的术语。]]
因为我不喜欢“你不能”的答案,这里有一些代码可以满足你的需求...
use strict;
use warnings;
BEGIN {
package MooX::MakeOptional;
use Types::Standard qw( Maybe Any );
use Exporter::Shiny our @EXPORT = qw( make_optional has );
use namespace::clean;
sub _exporter_validate_opts {
my $opts = pop;
$opts->{orig_has} = do {
no strict 'refs';
\&{ $opts->{into} . '::has' };
};
$opts->{attributes} = [];
'namespace::clean'->clean_subroutines( $opts->{into}, 'has' );
}
sub _generate_has {
my $opts = pop;
my $attributes = $opts->{attributes};
return sub {
my ( $name, %spec ) = @_;
if ( ref($name) eq 'ARRAY' ) {
push @$attributes, $_, { %spec } for @$name;
}
else {
push @$attributes, $name, \%spec;
}
};
}
sub _generate_make_optional {
my $opts = pop;
my $attributes = $opts->{attributes};
my $orig_has = $opts->{orig_has};
return sub {
my %optional;
$optional{$_} = 1 for @_;
while ( @$attributes ) {
my ( $name, $spec ) = splice( @$attributes, 0, 2 );
if ( $optional{$name} ) {
$spec->{isa} = Maybe[ $spec->{isa} or Any ];
}
$orig_has->( $name, %$spec );
}
}
}
}
{
package C;
use Moo;
use MooX::MakeOptional;
use Types::Standard qw( Str Int );
has 'x' => ( is => 'rw', isa => Str );
has 'y' => ( is => 'rw', isa => Int );
has 'z' => ( is => 'rw', isa => Int );
make_optional( qw(x y) );
}
它所做的是用一个虚拟替换替换 Moo 的 has
关键字,它除了将属性定义存储到数组中外什么都不做。
然后当 make_optional
被调用时,它遍历数组,并将每个属性定义传递给 Moo 的原始 has
关键字,但如果指定则更改为可选。
类 use MooX::MakeOptional
总是 需要确保他们在 class 定义的末尾调用 make_optional
函数, 即使它们没有可选属性。如果他们没有可选属性,他们应该只调用 make_optional
并传递一个空列表。