19

MooseX::Method::Signaturesについては多くの話題があり、それ以前にも、メソッドや関数のすべての引数を型チェックするように設計されたParams::Validateなどのモジュールが話題になっています。個人的にも職場でも、将来のすべての Perl コードに前者を使用することを検討しています。しかし、それが努力する価値があるかどうかはわかりません。

私が見た (そして書いた) すべての Perl コードは、そのようなチェックを実行していません。モジュールがこれを行うことはめったにありません。

my ($a, $b) = @_;
defined $a or croak '$a must be defined!';
!ref $a or croak '$a must be a scalar!";
...
@_ == 2 or croak "Too many arguments!";

use warnings;おそらく、ある種のヘルパー モジュールがないと単純に作業が多すぎるためですが、実際には、関数に余分な引数を送信したり、スカラーを期待するメソッドに配列参照を送信したりしないためです。私たちはすぐにそれについて耳にします - ダックタイピングアプローチ.

では、Perl の型チェックはパフォーマンス ヒットに値するものでしょうか? それとも、C や Java などのコンパイルされた厳密に型指定された言語で主にその強みが発揮されるのでしょうか?

これらのモジュールを使用する Perl を書いた経験があり、それらの使用による利点 (または利点) を見た人からの回答に興味があります。会社/プロジェクトに型チェックに関するポリシーがある場合; 型チェックとパフォーマンスに関する問題。

更新:最近、 Strong Testing vs. Strong Typingという興味深い記事を読みました。わずかな Python の偏見を無視して、本質的には型チェックが場合によっては窒息する可能性があり、プログラムが型チェックに合格したとしても、それが正確であることを保証するものではないと述べています。

4

9 に答える 9

14

引数がまさに必要なものであることを確認することが重要な場合は、それだけの価値があります。パフォーマンスが問題になるのは、すでに適切に機能している場合のみです。間違った答えやコア ダンプをどれだけ早く取得できるかは重要ではありません。:)

さて、それはばかげたことのように聞こえますが、そうでない場合を考えてみてください。ここに何が入っているか本当に気にし@_ますか?

sub looks_like_a_number { $_[0] !~ /\D/ }
sub is_a_dog            { eval { $_[0]->DOES( 'Dog' ) } }

これらの 2 つの例では、引数が期待どおりでない場合でも、無効な引数はテストに合格しないため、正しい答えが得られます。それを醜いと考える人もいますが、私も彼らの言い分は理解できますが、別の方法も醜いと思います。誰が勝ちますか?

ただし、状況がそれほど単純ではないため、ガード条件が必要になる場合があります。次にデータを渡さなければならないのは、データが特定の範囲内または特定の型にあり、エレガントに失敗しないことを期待する場合です。

ガード条件について考えるとき、入力が悪い場合に何が起こるか、失敗をどれだけ気にするかを考えます。それぞれの状況の要求によってそれを判断しなければなりません。私はそれが答えとしてひどいことを知っていますが、それが問題ではない場合でも、すべての混乱を経験しなければならない束縛と規律のアプローチよりも好きな傾向があります.

Params::Validateそのコードはしばしば私のサブルーチンよりも長いので、私は恐れています。Moose のものは非常に魅力的ですが、これは自分が望むものを宣言する方法であり、手で構築できるものを手に入れる方法であることを認識しておく必要があります (ただ、それを見たり実行したりする必要はありません)。私が Perl で最も嫌いな点は、オプションのメソッド シグネチャがないことです。これは、Perl 6 と Moose の最も魅力的な機能の 1 つです。

于 2010-02-25T00:56:21.847 に答える
7

私は基本的にブライアンに同意します。メソッドの入力についてどれだけ心配する必要があるかは、a) 誰かが不正なデータを入力すること、および b) 不正なデータがメソッドの目的を損なうことをどの程度懸念しているかに大きく依存します。また、外部メソッドと内部メソッドには違いがあることも付け加えておきます。クラスの消費者に約束をしているので、パブリック メソッドについてはもっと注意を払う必要があります。逆に、内部メソッドにアクセスするコードを (理論的には) より細かく制御できるため、内部メソッドについてはそれほど注意を払うことができず、問題が発生した場合に責任を負うのは自分だけです。

MooseX::Method::Signatures は、メソッドのパラメーターを説明する単純な宣言方法を追加するための洗練されたソリューションです。Method::Signatures::Simple と Params::Validate は優れていますが、Moose の最も魅力的な機能の 1 つである Type システムが欠けています。私はいくつかのプロジェクトで MooseX::Declare を使用し、MooseX::Method::Signatures を拡張して使用しましたが、追加のチェックを作成するためのハードルが非常に小さく、ほとんど魅惑的であることがわかりました。

于 2010-02-25T01:53:06.287 に答える
6

はい、それだけの価値があります。防御的なプログラミングは、常に価値のあるものの1つです。

于 2011-12-15T01:24:00.467 に答える
4

私が見たこれに対する反論は、関数呼び出しごとにパラメーターをチェックするのは冗長であり、CPU 時間の無駄だというものです。この引数の支持者は、すべての着信データが最初にシステムに入るときに厳密にチェックされるモデルを好みますが、内部メソッドは、システムのチェックに既に合格したデータを渡すコードによってのみ呼び出される必要があるため、パラメーター チェックがありません。境界なので、まだ有効であると想定されます。

理論的には、私はその音をとても気に入っていますが、誰かがシステムを使用する (または使用できるようにするためにシステムを拡張する必要がある) 場合、システムが予想外の方法で使用された場合、カードの家のように簡単に崩壊する可能性があることもわかります。最初の検証境界が確立されます。内部関数への 1 つの外部呼び出しだけで、すべての賭けがオフになります。

実際には、私は現在 Moose を使用していますが、Moose は属性レベルで検証をバイパスするオプションを実際には提供していません。また、MooseX::Declare は @_ を手動で展開するよりも手間がかからずにメソッド パラメータを処理および検証します。それはかなり議論の余地のあるポイントです。

于 2010-02-25T09:10:15.957 に答える
2

I want to mention two points here. The first are the tests, the second the performance question.

1) Tests

You mentioned that tests can do a lot and that tests are the only way to be sure that your code is correct. In general i would say this is absolutly correct. But tests itself only solves one problem.

If you write a module you have two problems or lets say two different people that uses your module.

You as a developer and a user that uses your module. Tests helps with the first that your module is correct and do the right thing, but it didn't help the user that just uses your module.

For the later, i have one example. i had written a module using Moose and some other stuff, my code ended always in a Segmentation fault. Then i began to debug my code and search for the problem. I spend around 4 hours of time to find the error. In the end the problem was that i have used Moose with the Array Trait. I used the "map" function and i didn't provide a subroutine function, just a string or something else.

Sure this was an absolutly stupid error of mine, but i spend a long time to debug it. In the end just a checking of the input that the argument is a subref would cost the developer 10 seconds of time, and would cost me and propably other a lot of more time.

I also know of other examples. I had written a REST Client to an interface completly OOP with Moose. In the end you always got back Objects, you can change the attributes but sure it didn't call the REST API for every change you did. Instead you change your values and in the end you call a update() method that transfers the data, and change the values.

Now i had a user that then wrote:

$obj->update({ foo => 'bar' })

Sure i got an error back, that update() does not work. But sure it didn't work, because the update() method didn't accept a hashref. It only does a synchronisation of the actual state of the object with the online service. The correct code would be.

$obj->foo('bar');
$obj->update();

The first thing works because i never did a checking of the arguments. And i don't throw an error if someone gives more arguments then i expect. The method just starts normal like.

sub update {
  my ( $self ) = @_;
  ...
}

Sure all my tests absolutely works 100% fine. But handling these errors that are not errors cost me time too. And it costs the user propably a lot of more time.

So in the end. Yes, tests are the only correct way to ensure that your code works correct. But that doesn't mean that type checking is meaningless. Type checking is there to help all your non-developers (on your module) to use your module correctly. And saves you and others time finding dump errors.

2) Performance

The short: You don't care for performance until you care.

That means until your module works to slow, Performance is always fast enough and you don't need to care for this. If your module really works to slow you need further investigations. But for these investigions you should use a profiler like Devel::NYTProf to look what is slow.

And i would say. In 99% slowliness is not because you do type checking, it is more your algorithm. You do a lot of computation, calling functions to often etc. Often it helps if you do completly other solutions use another better algorithm, do caching or something else, and the performance hit is not your type checking. But even if the checking is the performance hit. Then just remove it where it matters.

There is no reason to leave the type checking where performance don't matters. Do you think type checking does matter in a case like above? Where i have written a REST Client? 99% of performance issues here are the amount of request that goes to the webservice or the time for such an request. Don't using type checking or MooseX::Declare etc. would propably speed up absolutly nothing.

And even if you see performance disadvantages. Sometimes it is acceptable. Because the speed doesn't matter or sometimes something gives you a greater value. DBIx::Class is slower then pure SQL with DBI, but DBIx::Class gives you a lot for these.

于 2010-05-12T09:23:32.730 に答える
1

はい、開発、保守、デバッグなどの際に役立つため、絶対に価値があります。

開発者が誤って間違ったパラメータをメソッドに送信した場合、エラーが別の場所に伝播される代わりに、有用なエラーメッセージが生成されます。

于 2012-07-03T12:32:09.313 に答える
1

Params::Validate はうまく機能しますが、もちろん引数をチェックすると処理が遅くなります。テストは必須です (少なくとも私が書いたコードでは)。

于 2010-03-10T07:55:26.507 に答える
0

私が取り組んでいるかなり大規模な OO プロジェクトで Moose を広範囲に使用しています。Moose の厳密な型チェックのおかげで、いくつかのケースでベーコンを節約できました。最も重要なことは、「undef」値がメソッドに誤って渡される状況を回避するのに役立ったことです。これらのインスタンスだけでも、何時間ものデバッグ時間を節約できました..

パフォーマンスへの影響は確かにありますが、管理できます。NYTProf を 2 時間使用することで、苦労しすぎていたいくつかの Moose Attributes を見つけることができ、コードをリファクタリングしたところ、パフォーマンスが 4 倍向上しました。

型チェックを使用します。防御コーディングはそれだけの価値があります。

パトリック。

于 2011-12-15T22:07:15.990 に答える
0

時々。私は通常、ハッシュまたはハッシュリファレンスを介してオプションを渡すときはいつでもそれを行います。このような場合、オプション名を間違って覚えたりスペルを間違えたりするのは非常に簡単Params::Checkです。

例えば:

sub revise {
    my ($file, $options) = @_;

    my $tmpl = {
        test_mode => { allow => [0,1], 'default' => 0 },
        verbosity => { allow => qw/^\d+$/, 'default' => 1 },
        force_update => { allow => [0,1], 'default' => 0 },
        required_fields => { 'default' => [] },
        create_backup => { allow => [0,1], 'default' => 1 },
    };

    my $args = check($tmpl, $options, 1)
      or croak "Could not parse arguments: " . Params::Check::last_error();
    ...
}

require_backupこれらのチェックを追加する前に、名前にアンダースコアまたはハイフンが使用されているか、 の代わりにpassが使用されているかなどを忘れていましたcreate_backup。これは私が自分で書いたコードのためのものです。ばか校正。型チェック、許容値チェック、デフォルト値、必須オプション、オプション値の他の変数への保存などをかなり簡単に行うことができます。Params::Check

于 2013-02-13T21:07:22.367 に答える