Pourquoi les prototypes de fonctions de Perl 5 sont-ils mauvais?

Dans une autre question de Stack Overflow, Leon Timmermans a affirmé:

Je vous conseille de ne pas utiliser de prototypes. Ils ont leurs utilisations, mais pas dans la plupart des cas et certainement pas dans celle-ci.

Pourquoi cela pourrait-il être vrai (ou autre)? Je fournis presque toujours des prototypes pour mes fonctions Perl, et je n’ai jamais vu personne d’autre dire quoi que ce soit à propos de leur utilisation.

Les prototypes ne sont pas mauvais s’ils sont utilisés correctement. La difficulté est que les prototypes de Perl ne fonctionnent pas comme les gens s’y attendent souvent. Les personnes ayant de l’expérience dans d’autres langages de programmation ont tendance à s’attendre à ce que les prototypes fournissent un mécanisme pour vérifier que les appels de fonctions sont corrects, c’est-à-dire qu’ils ont le bon nombre et le bon type d’arguments. Les prototypes de Perl ne sont pas adaptés à cette tâche. C’est le mauvais usage qui est mauvais. Les prototypes de Perl ont un objective unique et très différent:

Les prototypes vous permettent de définir des fonctions qui se comportent comme des fonctions intégrées.

  • Les parenthèses sont facultatives.
  • Le contexte est imposé aux arguments.

Par exemple, vous pouvez définir une fonction comme celle-ci:

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

et l’appeler comme

 mypush @array, 1, 2, 3; 

sans avoir besoin d’écrire le \ pour prendre une référence au tableau.

En résumé, les prototypes vous permettent de créer votre propre sucre syntaxique. Par exemple, le framework Moose les utilise pour émuler une syntaxe OO plus typique.

Ceci est très utile mais les prototypes sont très limités:

  • Ils doivent être visibles à la compilation.
  • Ils peuvent être contournés.
  • La propagation du contexte aux arguments peut provoquer un comportement inattendu.
  • Ils peuvent rendre difficile l’appel de fonctions en utilisant autre chose que le formulaire ssortingctement prescrit.

Voir Prototypes en perlsub pour tous les détails.

Le problème est que les prototypes de fonctions de Perl ne font pas ce que les gens pensent faire. Leur but est de vous permettre d’écrire des fonctions qui seront analysées comme les fonctions intégrées de Perl.

Tout d’abord, les appels de méthode ignorent complètement les prototypes. Si vous faites de la programmation OO, peu importe le prototype de vos méthodes. (Donc ils ne devraient pas avoir de prototype.)

Deuxièmement, les prototypes ne sont pas ssortingctement appliqués. Si vous appelez un sous-programme avec &function(...) , le prototype est ignoré. Donc, ils ne fournissent pas vraiment de sécurité de type.

Troisièmement, ce sont des actions fantasmagoriques à distance. (En particulier le prototype $ , ce qui fait que le paramètre correspondant est évalué dans un contexte scalaire, au lieu du contexte de liste par défaut.)

En particulier, ils compliquent la transmission des parameters depuis les tableaux. Par exemple:

 my @array = qw(abc); 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]); 

estampes:

 abc ab abc 3 b abc 

avec 3 avertissements sur main::foo() called too early to check prototype (si les avertissements sont activés). Le problème est qu’un tableau (ou une tranche de tableau) évalué dans un contexte scalaire renvoie la longueur du tableau.

Si vous avez besoin d’écrire une fonction qui fonctionne comme un intégré, utilisez un prototype. Sinon, n’utilisez pas de prototypes.

Note: Perl 6 aura des prototypes complètement remaniés et très utiles. Cette réponse ne concerne que Perl 5.

Je suis d’accord avec les deux affiches ci-dessus. En général, utiliser $ devrait être évité. Les prototypes ne sont utiles que lorsque vous utilisez des arguments de bloc ( & ), des globs ( * ) ou des prototypes de référence ( \@ , \$ , \% , \* )

Certaines personnes, en regardant un prototype de sous-programme Perl, pensent que cela signifie quelque chose qui ne fonctionne pas:

 sub some_sub ($$) { ... } 

Pour Perl, cela signifie que l’parsingur attend deux arguments. C’est la manière dont Perl vous permet de créer des sous-routines qui se comportent comme des composants intégrés, qui savent tous ce qui est attendu du code suivant. Vous pouvez lire sur les prototypes dans perlsub

Sans lire la documentation, les gens supposent que les prototypes font référence à la vérification des arguments d’exécution ou à quelque chose de similaire qu’ils ont vu dans d’autres langues. Comme avec la plupart des choses que les gens pensent de Perl, ils se révèlent être faux.

Cependant, à partir de Perl v5.20, Perl a une fonctionnalité, expérimentale au moment où j’écris ceci, qui donne quelque chose de plus comme ce que les utilisateurs attendent et quoi. Les signatures de sous – routine de Perl exécutent le comptage des arguments, l’atsortingbution de variables et les parameters par défaut:

 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"; } 

Ceci est la fonctionnalité que vous voulez probablement si vous envisagez de prototypes.