Perl 5の関数プロトタイプがなぜ悪いのですか?


116

、別のスタックオーバーフローの質問 レオンTimmermansがアサート:

プロトタイプを使用しないことをお勧めします。それらには用途がありますが、ほとんどの場合ではなく、間違いなくこの場合ではありません。

なぜこれが本当なのか(またはそうでないのか)?私はほとんど常に自分のPerl関数のプロトタイプを提供します。他の誰かがそれらの使用について何か悪いことを言うのを見たことがありません。


私も知りたいです。それらを使用しないのは、可変数の引数を指定して呼び出す場合のみです。
Paul Tomblin、

7
「Perlプロトタイプは有害だと見なされていますという記事を読むことをお勧めしますか?
tchrist

回答:


121

プロトタイプは、正しく使用すれば問題ありません。難点は、Perlのプロトタイプが、人々が期待するように機能しないことです。他のプログラミング言語のバックグラウンドを持つ人々は、プロトタイプが関数呼び出しが正しいことを確認するためのメカニズムを提供することを期待する傾向があります。つまり、引数の数とタイプが正しいことです。Perlのプロトタイプはこのタスクにはあまり適していません。悪いのは誤用です。Perlのプロトタイプには、単一の非常に異なる目的があります。

プロトタイプを使用すると、組み込み関数のように動作する関数を定義できます。

  • 括弧はオプションです。
  • 引数にはコンテキストが課せられます。

たとえば、次のような関数を定義できます。

sub mypush(\@@) { ... }

そしてそれを

mypush @array, 1, 2, 3;

\配列への参照を取得するためにを記述する必要はありません。

一言で言えば、プロトタイプを使用すると、独自の構文糖を作成できます。たとえば、Mooseフレームワークはそれらを使用して、より一般的なOO構文をエミュレートします。

これは非常に便利ですが、プロトタイプは非常に限られています。

  • それらはコンパイル時に表示される必要があります。
  • それらはバイパスすることができます。
  • コンテキストを引数に伝播すると、予期しない動作が発生する可能性があります。
  • 厳密に規定された形式以外を使用して関数を呼び出すことが困難になる場合があります。

参照してくださいプロトタイプを、すべての血みどろの詳細についてはperlsubに。


2
私はこの答えを受け入れました。なぜなら、それが質問に最もよく答えると感じたからです。
アルニタク

2
一方、Mooseのプロトタイプは/ awesome / p3rl.org/MooseX::Declare p3rl.org/MooseX::Method::Signatures
Kent Fredric


それで、彼らはそれで間違った名前ですか?
Peter Mortensen

69

問題は、Perlの関数プロトタイプが、人々が思っていることを実行しないことです。その目的は、Perlの組み込み関数のように解析される関数を作成できるようにすることです。

まず、メソッド呼び出しはプロトタイプを完全に無視します。オブジェクト指向プログラミングをしている場合、メソッドがどのプロトタイプを持っているかは問題ではありません。(したがって、プロトタイプは必要ありません。)

第二に、プロトタイプは厳密には強制されません。でサブルーチンを呼び出すと&function(...)、プロトタイプは無視されます。したがって、実際には型の安全性は提供されません。

第三に、彼らは距離で不気味な行動です。(特に$プロトタイプ。これにより、対応するパラメーターがデフォルトのリストコンテキストではなくスカラーコンテキストで評価されます。)

特に、配列からパラメーターを渡すことが難しくなります。例えば:

my @array = qw(a b c);

foo(@array);
foo(@array[0..1]);
foo($array[0], $array[1], $array[2]);

sub foo ($;$$) { print "@_\n" }

foo(@array);
foo(@array[0..1]);
foo($array[0], $array[1], $array[2]);

プリント:

a b c
a b
a b c
3
b
a b c

に関する3つの警告main::foo() called too early to check prototype(警告が有効になっている場合)とともに。問題は、スカラーコンテキストで評価された配列(または配列スライス)が配列の長さを返すことです。

組み込みのように機能する関数を作成する必要がある場合は、プロトタイプを使用します。それ以外の場合は、プロトタイプを使用しないでください。

注:Perl 6は完全に改良された非常に便利なプロトタイプになります。この回答はPerl 5にのみ適用されます。


しかし、それらはまだあなたの呼び出し元とサブルーチンが同じ数の引数を使用していることの有用なチェックを提供します、それでそれの何が問題になっていますか?
Paul Tomblin、

2
番号; 一般的なコンセンサスは、Perl関数プロトタイプは本質的に何の利点も提供しないということです。少なくともPerl 5では、それらを気にする必要はありません。Perl6は別の(より良い)ストーリーかもしれません。
ジョナサンレフラー

5
このようPARAMS ::検証モジュールとして検証引数、より良い方法がありますsearch.cpan.org/~drolsky/Params-Validate-0.91/lib/Params/...は
friedo

10
修正:配列スライスはリストを返すため、スカラーコンテキストの配列スライスはリストの最後の要素を返します。最後から2番目のfoo()prints 2の呼び出しは、2つの要素のスライスの最後の要素であるためです。に変更するmy @array = qw(foo bar baz)と、違いがわかります。(余談ですが、これがスローアウェイ型の実証的なコードで配列またはリストを0または1ベースの数値シーケンスに初期化しない理由です。コンテキスト内のインデックス、カウント、および要素間の混乱により、何度も悩まされてきました。愚かだが真実。)
ピルクロー

2
@pilcrow:私a b cはあなたのポイントをより明確にするために使用する回答を編集しました。
Flimm 2014年

30

上記の2枚のポスターに同意します。一般に、使用$は避けるべきです。ブロック引数を(使用する場合のプロトタイプのみ有用である&)、グロブ(*)、または基準プロトタイプ(\@\$\%\*


一般的に、多分、しかし私は2つの例外に言及したいと思います:最初に、($)プロトタイプは名前付きの単項演算子を作成します。次に、組み込みをオーバーライドする場合(インポートまたはCORE :: GLOBAL ::を使用して)、組み込みのプロトタイプに固執する$必要があります。でも)リストコンテキストでは、組み込みの場合はスカラーコンテキストが提供されます。
Sidhekin

4

一部の人々は、Perlサブルーチンのプロトタイプを見て、それが意味のないことを意味すると考えています。

sub some_sub ($$) { ... }

Perlにとって、これはパーサーが2つの引数を期待することを意味します。これは、ビルトインのように動作するサブルーチンを作成するためのPerlの方法であり、すべてが後続のコードから何を期待するかを認識しています。あなたはperlsubでプロトタイプについて読むことができます

ドキュメントを読まなければ、プロトタイプはランタイムの引数チェックまたは他の言語で見たものと同様のものを参照していると思われます。ほとんどの人がPerlについて推測するように、彼らは間違っていることが判明しました。

ただし、Perl v5.20以降、Perlには、私がこれを書いている時点で実験的な機能があり、ユーザーが何を期待し、何を期待するかのようなものを提供します。Perlのサブルーチンシグネチャは、実行時の引数のカウント、変数の割り当て、およびデフォルト設定を行います。

use v5.20;
use feature qw(signatures);
no warnings qw(experimental::signatures);

animals( 'Buster', 'Nikki', 'Godzilla' );

sub animals ($cat, $dog, $lizard = 'Default reptile') { 
    say "The cat is $cat";
    say "The dog is $dog";
    say "The lizard is $lizard";
    }

これは、プロトタイプを検討している場合におそらく必要な機能です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.