¿Por qué los prototipos de función de Perl 5 son malos?

En otra pregunta de Stack Overflow, Leon Timmermans afirmó:

Te aconsejaría que no uses prototipos. Tienen sus usos, pero no para la mayoría de los casos y definitivamente no en este.

¿Por qué esto podría ser cierto (o no)? Casi siempre proporciono prototipos para mis funciones de Perl, y nunca antes había visto a nadie decir algo malo sobre su uso.

Los prototipos no son malos si se usan correctamente. La dificultad es que los prototipos de Perl no funcionan de la forma en que la gente a menudo espera que lo hagan. Las personas con experiencia en otros lenguajes de progtwigción tienden a esperar que los prototipos proporcionen un mecanismo para verificar que las llamadas a funciones sean correctas: es decir, que tengan el número y el tipo de argumentos correctos. Los prototipos de Perl no son adecuados para esta tarea. Es el mal uso que es malo. Los prototipos de Perl tienen un propósito singular y muy diferente:

Los prototipos le permiten definir funciones que se comportan como funciones incorporadas.

  • Los paréntesis son opcionales.
  • El contexto se impone a los argumentos.

Por ejemplo, podría definir una función como esta:

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

y llámalo como

 mypush @array, 1, 2, 3; 

sin necesidad de escribir \ para tomar una referencia a la matriz.

En pocas palabras, los prototipos te permiten crear tu propio azúcar sintáctico. Por ejemplo, el framework Moose los usa para emular una syntax OO más típica.

Esto es muy útil, pero los prototipos son muy limitados:

  • Deben estar visibles en tiempo de comstackción.
  • Ellos pueden ser pasados ​​por alto.
  • Propagar el contexto a los argumentos puede provocar un comportamiento inesperado.
  • Pueden hacer que sea difícil llamar a funciones usando cualquier cosa que no sea la forma estrictamente prescrita.

Ver Prototipos en perlsub para todos los detalles sangrientos.

El problema es que los prototipos de función de Perl no hacen lo que las personas piensan que hacen. Su propósito es permitirle escribir funciones que serán analizadas como las funciones integradas de Perl.

En primer lugar, las llamadas a métodos ignoran por completo los prototipos. Si estás haciendo progtwigción OO, no importa qué prototipo tengan tus métodos. (Entonces no deberían tener ningún prototipo).

En segundo lugar, los prototipos no se aplican estrictamente. Si llama a una subrutina con &function(...) , el prototipo se ignora. Entonces, realmente no proporcionan ningún tipo de seguridad.

En tercer lugar, son acciones escalofriantes a distancia. (Especialmente el prototipo $ , que hace que el parámetro correspondiente se evalúe en contexto escalar, en lugar del contexto de lista predeterminado.)

En particular, dificultan la transferencia de parámetros desde matrices. Por ejemplo:

 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]); 

huellas dactilares:

 abc ab abc 3 b abc 

junto con 3 advertencias sobre main::foo() called too early to check prototype (si las advertencias están habilitadas). El problema es que una matriz (o porción de matriz) evaluada en contexto escalar devuelve la longitud de la matriz.

Si necesita escribir una función que funciona como un built-in, use un prototipo. De lo contrario, no use prototipos.

Nota: Perl 6 tendrá prototipos completamente renovados y muy útiles. Esta respuesta se aplica solo a Perl 5.

Estoy de acuerdo con los dos carteles anteriores. En general, se debe evitar usar $ . Los prototipos solo son útiles cuando se usan argumentos de bloque ( & ), globs ( * ) o prototipos de referencia ( \@ , \$ , \% , \* )

Algunas personas, mirando un prototipo de subrutina de Perl, piensan que significa algo que no:

 sub some_sub ($$) { ... } 

Para Perl, eso significa que el analizador espera dos argumentos. Es la forma de Perl de permitirle crear subrutinas que se comporten como integradas, todas las cuales saben qué esperar del siguiente código. Puedes leer sobre prototipos en perlsub

Sin leer la documentación, la gente adivina que los prototipos se refieren a la comprobación de los argumentos de tiempo de ejecución o algo similar que han visto en otros idiomas. Como con la mayoría de las cosas que las personas adivinan sobre Perl, resultan estar equivocadas.

Sin embargo, comenzando con Perl v5.20, Perl tiene una característica, experimental mientras escribo esto, que da algo más como lo que los usuarios esperan y qué. Las firmas de subrutina de Perl ejecutan el recuento de argumentos de tiempo, la asignación de variables y la configuración predeterminada:

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

Esta es la característica que probablemente quiera si está considerando prototipos.