3

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でそれを行う方法は?

4

2 に答える 2

5

できません。

Moo には Meta Object Protocol がありません。それがなければ、戻って何かを変更することはできません。

メタ オブジェクトはありません。このレベルの複雑さが必要な場合は、Moose が必要です。Moo は、明示的にメタプロトコルを提供しないため小さいです。

さらに、型は単なるコード参照です。

組み込み型システムはありません。isa は coderef で検証されます。複雑な型が必要な場合、Type::Tiny は型とタイプ ライブラリを提供でき、Moo と Moose の両方でシームレスに動作します。


あなたができることは、何らかのシングルトンにアクセスしてMaybe[Str]orStrのように動作するかどうかを決定する型を書くことかもしれませんが、それはロングショットであり、おそらく醜くてクレイジーなので、やるべきではありません。

于 2016-09-16T13:25:17.027 に答える
2

[[Maybe型は実際には属性自体をオプションにするのではなく、未定義耐性を持たせることに注意してください。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 オプションの属性がない場合でも、クラスmake_optional定義の最後で関数を呼び出す必要があるクラス。オプションの属性がない場合は、空のリストを呼び出して渡すだけです。make_optional

于 2020-11-05T16:36:58.150 に答える