Mostrando entradas con la etiqueta Estilo. Mostrar todas las entradas
Mostrando entradas con la etiqueta Estilo. Mostrar todas las entradas

viernes, 18 de septiembre de 2009

Perl inteligente

[English translation]
En el artículo anterior vimos un ejemplo de Perl moderno, hoy vamos a profundizar un poquito más en la comparación inteligente de Perl 5.10 y como al combinarla con las características dinámicas del lenguaje obtenemos un programa ridículamente pequeño, pero más fácil de comprender y mantener.
Alguna vez leí (creo que de Paul Graham) que cuando alguna sección de código parece duplicada generalmente hace falta un nivel de abstracción, claro que él programa en Lisp, y tiene defmacro. Sin embargo Perl también tiene lo suyo, y en este caso nuestra primera solución podría basarse en un hash que incluya las funciones permitidas en nuestra calculadora:
 1 #!/usr/bin/perl
 2 
 3 use Modern::Perl;
 4 use Scalar::Util qw( looks_like_number );
 5 use Statistics::Descriptive;
 6 
 7 use constant SYNTAX_ERROR => "Error: tipee 'help' para ayuda";
 8 
 9 my %FUNCS = (
10     sum                => 0,
11     mean               => 0,
12     count              => 0,
13     variance           => 0,
14     standard_deviation => 0,
15     min                => 0,
16     mindex             => 0,
17     max                => 0,
18     maxdex             => 0,
19     sample_range       => 0,
20     median             => 0,
21     harmonic_mean      => 0,
22     geometric_mean     => 0,
23     mode               => 0,
24     trimmed_mean       => 0,
25 );
26 
27 my $s = Statistics::Descriptive::Full->new();
28 while (1) {
29     print "Listo> ";
30     my $command = readline(STDIN) // last;
31     $command =~ s/^\s+//; $command =~ s/\s+$//;
32     given ($command) {
33         when ( looks_like_number($_) ) { $s->add_data($command) }
34         when (/^(exit|quit)$/)         {last}
35         default {
36             if   ( exists $FUNCS{$command} ) { ... }
37             else                             { say SYNTAX_ERROR}
38         }
39     }
40 }
Esto es un avance importante, porque estamos simplificando el código en la parte complicada del programa, y reemplazándolo por una simple declaración de un hash, donde incluir una nueva función es tan simple como agregar una línea.
Claro que cualquier lector astuto se ha dado cuenta de que hago trampa porque el programa está incompleto; la línea 36 necesita una acción, nuestro problema ahora es como ejecutar el método correcto para la operación, y en Perl como de costumbre hay varias formas de hacerlo, una de ellas (la peor) podría utilizar referencias a las funciones de la clase en el hash, así:

10     sum  => \&Statistics::Descriptive::sum,
Para luego hacer algo como:

36     if ( exists $FUNCS{$command} ) { say "$command = " . $FUNCS{$command}($s) }
Digo que esa es la peor forma porque hay que saber mucho Perl para entender como funciona eso, y Perl tiene la capacidad de despachar métodos simbólicamente haciendo que nuestra intención quede perfectamente clara:
36     if ( exists $FUNCS{$command} ) { say "$command = " . $s->$command }
El costo de esta operación es mayor que el de la alternativa anterior, sin embargo es un precio que se paga con gusto, porque el programa es mucho más fácil de entender, y últimamente la gente es mucho más cara que las máquinas.
Finalmente si la flojera es uno de tus principios fundamentales, puedes reescribir la asignación del hash así:
 9 my %FUNCS = map { $_ => 0 } qw( sum mean count variance standard_deviation
10     min mindex max maxdex sample_range median harmonic_mean geometric_mean
11     mode trimmed_mean );
Lo que particularmente aprecio, porque ahorro puntuación (lo que parece abrumar a mucha gente) y tengo menos probabilidad de cometer un error de sintaxis.
Básicamente estoy construyendo una lista de palabras (los nombres de los métodos) con "qw", a partir de esta lista construyo otra (usando map) que contiene cada elemento de la lista original ($_) acompañado del número  0, perl convierte automáticamente esta lista en un hash donde cada nombre tiene asociado 0 como valor.
Si la explicación anterior te parece complicada o incomprensible, puedes ver la documentación de map en Perl, lo que te vendrá de maravilla porque además podrás aprender algo de programación funcional que de seguro te será muy provechoso.
Ahora me voy a deshacer del "if", prefiero los condicionales de múltiples vias, son más planos y se ve mejor el flujo, por eso opino que el given/when es lo mejor que le ha pasado a Perl en mucho tiempo, además me voy a deshacer de la expresión regular en la línea 34 por algo que tenga más sentido para un extraño a Perl:
 1 #!/usr/bin/perl
 2 
 3 use Modern::Perl;
 4 use Scalar::Util qw( looks_like_number );
 5 use Statistics::Descriptive;
 6 
 7 use constant SYNTAX_ERROR => "Error: tipee 'help' para ayuda";
 8 
 9 my %FUNCS = map { $_ => 1 } qw( sum mean count variance standard_deviation
10     min mindex max maxdex sample_range median harmonic_mean geometric_mean
11     mode trimmed_mean );
12 
13 my $s = Statistics::Descriptive::Full->new();
14 while (1) {
15     print "Listo> ";
16     my $command = readline(STDIN) // last;
17     $command =~ s/^\s+//; $command =~ s/\s+$//;
18     given ($command) {
19         when ( looks_like_number($_) ) { $s->add_data($command) }
20         when ( ["exit", "quit"] )      {last}
21         when (%FUNCS)                  { say "$command = " . $s->$command }
22         default                        { say SYNTAX_ERROR }
23     }
24 }
Ahora se ve mucho mejor (hasta parece Erlang).
Me estoy valiendo de algunas  funciones del smart matching que explicaré a continuación.
En la línea 20 se compara un valor contra un arreglo:
$command ~~ ["exit", "quit"]
Cuando se compara un escalar (a la izquierda) contra un arreglo (a la derecha) el efecto es equivalente a lo siguiente:
sub match_scalar_arrayref {
    my ($scalar, $arrayref) = @_;
    for my $item ( @$arrayref ) {
        return 1 if $scalar eq $item;
    }
    return undef;
}

Ya no recuerdo cuantas veces he escrito código como ese, o como este:
if ( grep { $scalar eq $_ } @$arrayref ) ...
Y que  ahora podré escribir con mas claridad y menos esfuerzo:
if ( $scalar ~~ $arrayref ) ...
Probablemente ya adivinaste que en la línea 21, el smart match entre un escalar y un hash es equivalente a:
if ( exists $hash{$scalar} ) ...

Un poco de tentación

Una sugerencia que recibí de un lector daba una solución todavía más corta y fácil de mantener, la idea era cambiar la línea:

21    when (%FUNCS) { say "$command = " . $s->$command }
por:
21    when ($s->can($command)) { say "$command = " . $s->$command }
El método can es provisto por la clase UNIVERSAL, de la cual derivan todos los objetos en Perl, y el propósito de este método es averiguar si un objeto o clase tiene un método determinado.
Al utilizar esta mejora ni siquiera necesito el hash %FUNCS, y además nuestro interpretador se actualizará automáticamente con nuevos comandos a medida que evolucione Statistics::Descriptive, lo cual suena muy bien desde el punto de vista de mantenibilidad, sin embargo, tiene un problema fatal para mí: no es seguro.
El problema es que pierdo el control sobre lo que Perl ejecuta automáticamente, y aunque probablemente este módulo no pueda hacer mucho daño, esta misma técnica utilizando algún otro módulo, podría ser peligrosa. Así que prefiero la seguridad y me quedo con el hash como mecanismo de despacho (y autorización de uso).
La moraleja es que se debe terner cuidado al utilizar mecanismos de control de ejecución dinámicos, sobre todo cuando se utilizan datos de fuentes externas no confiables en estos mecanismos de control de ejecución.

Completando el programa


La línea 22 da un error cuando no se conoce un comando, el mensaje dice que use "help" para obtener ayuda, pero el comando "help" todavía no está implementado, una manera rápida de implementarlo es:

23         when ("help") {
24             say "Los comandos válidos son: "
25                 . join( ", ", qw(exit quit help), keys %FUNCS )
26         }
Guau, eso fue fácil, lo mejor es que además de fácil es consistente, porque se utiliza la misma estructura de datos para informar los comandos, para seleccionarlos y para autorizarlos.
Una de las funciones que se me olvidó incluir en la calculadora en el artículo anterior fue "clear", agregar esta función ahora es tan sencillo como poner una nueva palabra en la definición de %FUNCS:
 9 my %FUNCS = map { $_ => 1 } qw( sum mean count variance standard_deviation
10     min mindex max maxdex sample_range median harmonic_mean geometric_mean
11     mode trimmed_mean clear )
Fue fácil, ¿o no?. Lo mejor es que el nuevo comando aparece automáticamente en la ayuda porque el programa es consistente.
Recapitulemos los logros del día, tenemos un programa:
  • Muy compacto
  • Fácil de comprender
  • Fácil de mantener
  • Consistente
  • Seguro
Perl tan válido como cualquier otro lenguaje, pero además, muy pocos lenguajes brindan mecanismos similares a los aquí expuestos para lograr un programa con estas características.
En el próximo artículo veremos como agregarle el manual de funciones estadísticas a nuestra calculadora con mucha facilidad.
A continuación los dejo con la versión final del programa:
 1 #!/usr/bin/perl
 2 
 3 use Modern::Perl;
 4 use Scalar::Util qw( looks_like_number );
 5 use Statistics::Descriptive;
 6 
 7 use constant SYNTAX_ERROR => "Error: tipee 'help' para ayuda";
 8 
 9 my %FUNCS = map { $_ => 1 } qw( sum mean count variance standard_deviation
10     min mindex max maxdex sample_range median harmonic_mean geometric_mean
11     mode trimmed_mean clear );
12 
13 my $s = Statistics::Descriptive::Full->new();
14 while (1) {
15     print "Listo> ";
16     my $command = readline(STDIN) // last;
17     $command =~ s/^\s+//;
18     $command =~ s/\s+$//;
19     given ($command) {
20         when ( looks_like_number($_) ) { $s->add_data($command) }
21         when (%FUNCS)                  { say "$command = " . $s->$command }
22         when ( [ "exit", "quit" ] )    {last}
23         when ("help") {
24             say "Los comandos válidos son: "
25                 . join( ", ", qw(exit quit help), keys %FUNCS )
26         }
27         default { say SYNTAX_ERROR };
28     }
29 }

martes, 15 de septiembre de 2009

Usando Perl Moderno

[English translation]
Voy a intentar escribir una serie de artículos sobre Perl donde se pueda apreciar lo fácil y rápido que se pueden crear soluciones en esta plataforma.
Para ello elegí un diseño simple que me permite ilustrar una cantidad de técnicas y mejores prácticas, con un algoritmo accesible para cualquier programador aunque sea novato.
El programa de ejemplo será una calculadora estadística que en primera instancia utilizará un estilo tradicional, pero que se transformará poco a poco, haciéndose más flexible y fácil de mantener, mientras se aplican algunos mecanismos únicos del lenguaje y algunas librerías del CPAN.
El gran final es hacer que la calculadora sea una aplicación web utilizando un mecanismo sorprendente desarrollado sobre Perl. Una vez dicho esto comenzaré a usar Perl Moderno.
Haciendo honor al título del artículo, lo primero que hace nuestro programa es usar el módulo Modern::Perl, que es un atajo para decir:

use feature ':5.10';
use strict;
use warnings;
use mro 'c3';
Es decir, activa todas las características introducidas en Perl 5.10, también activa el modo estricto y las advertencias, y finalmente activa el orden de resolución de métodos utilizando el algoritmo C3. Como es de esperarse todos los ejemplos que veremos a lo largo de esta serie de artículos, solo funcionarán en Perl 5.10, porque intentare promover la mayor cantidad de características de esta nueva versión del lenguaje, así que: a instalar Perl 5.10.
Al usar Perl Moderno, se recomienda enfáticamente el uso de strict porque captura muchos errores comunes, entre ellos el uso accidental de referencias simbólicas, y los errores de tipográficos en las variables, al costo de que ahora deben ser declaradas con our (globales) ó my (léxicas), antes de usarse.
Por otra parte las advertencias permiten que perl nos informe acerca de posibles errores en la codificación. En perl 5.10 strict es más estricto y warnings tiene muchas advertencias nuevas, así que capturan más problemas que antes, lo que suele mejorar la calidad general del código y ahorrar tiempo de depuración.
En mi caso particular, cuando quise leer un comando y terminar el ciclo en caso de un fin de archivo escribí:

my $comando = readline(STDIN) or last;
Perl inmediatamente me advirtió que en algún caso podría confundirse undef (que denota el fin de archivo) con un "0" (cero), porque en perl "0" y undef se interpretan como falso. Una manera de escribir correctamente la instrucción sería:

defined (my $comando = readline(STDIN)) or last;
Pero aproveché para utilizar el nuevo operador // (defined or), con el que puedo escribir simplemente:

my $comando = readline(STDIN) // last;
El orden de resolución de métodos C3, resuelve algunos problemas existentes con el orden de resolución original de Perl, y lo recomendable es usarlo siempre en el nuevo código, esto no es del todo nuevo, hay módulos que usan este orden de resolución desde hace unos 4 años, solo que antes era un módulo del CPAN (Class::C3) y ahora tiene soporte nativo en el lenguaje.
Así que el primer consejo es usar Modern::Perl, porque activa una cantidad de características útiles y recomendadas de Perl.
Volviendo al programa, lo siguiente después de usar Modern::Perl es importar la subrutina looks_like_number() de Scalar::Util, que además de ahorrarme el trabajo de escribir las expresiones regulares para reconocer números, ahorra una buena cantidad de pánico de los lectores que podrían bloquearse de solo ver esas expresiones regulares.
El último módulo que se usa es el ingrediente principal de la calculadora, porque nunca pasó por mi mente escribir algoritmos de estadística, para eso existe el CPAN, que tiene de todo. En este caso usé Statistics::Descriptive, que sirve perfectamente a mi propósito.
En la línea 7 se declara una constante con un mensaje de error y en la 9 se define una variable con un objeto de la clase Statistics::Descriptive::Full, que será el estado de nuestra calculadora estádística, durante el ciclo de interpretación.
El ciclo principal es simple: lee un comando o se termina (last) si llegó el fin de archivo [línea 12], seguidamente se eliminan los espacios por la izquierda y la derecha del comando [línea 13], si el comando es un número se agrega al conjunto de datos del objeto Statistics::Descriptive::Full [línea 15] y si no, se selecciona y ejecuta un comando del interpretador.
La selección se hace con la nueva estructura de control de Perl 5.10 given/when [líneas 18-36] que efectúa smart matching entre el valor dado (given) y las clausulas de comparación (when). Como la comparación es "inteligente" depende de los operandos, y en general funciona como uno se lo imagina, sin embargo hay algunos casos rebuscados y nunca está de más leer los manuales.
Finalmente el nuevo operador say, que no es más que un print que emite un fin de línea, ayudando a evitar un montón de concatenaciones con "\n" y por ello contribuye con la claridad del código.

 1 #!/usr/bin/perl
 2 
 3 use Modern::Perl;
 4 use Scalar::Util qw( looks_like_number );
 5 use Statistics::Descriptive;
 6 
 7 use constant SYNTAX_ERROR => "Error: tipee 'help' para ayuda";
 8 
 9 my $s = Statistics::Descriptive::Full->new();
10 while (1) {
11     print "Listo> ";
12     my $command = readline(STDIN) // last;
13     $command =~ s/^\s+//; $command =~ s/\s+$//;
14     if ( looks_like_number($command) ) {
15         $s->add_data($command);
16     }
17     else {
18         given ($command) {
19             when ("sum")                { say "$command = " . $s->sum() }
20             when ("mean")               { say "$command = " . $s->mean() }
21             when ("count")              { say "$command = " . $s->count() }
22             when ("variance")           { say "$command = " . $s->variance() }
23             when ("standard_deviation") { say "$command = " . $s->standard_deviation() }
24             when ("min")                { say "$command = " . $s->min() }
25             when ("mindex")             { say "$command = " . $s->mindex() }
26             when ("max")                { say "$command = " . $s->max() }
27             when ("maxdex")             { say "$command = " . $s->maxdex() }
28             when ("sample_range")       { say "$command = " . $s->sample_range() }
29             when ("median")             { say "$command = " . $s->median() }
30             when ("harmonic_mean")      { say "$command = " . $s->harmonic_mean() }
31             when ("geometric_mean")     { say "$command = " . $s->geometric_mean() }
32             when ("mode")               { say "$command = " . $s->mode() }
33             when ("trimmed_mean")       { say "$command = " . $s->trimmed_mean() }
34             when (/^(exit|quit)$/)      {last}
35             default                     { say SYNTAX_ERROR }
36         }
37     }
38 }
Para usar la calculadora simplemente ejecutamos el archivo, aquí les muestro una corrida de prueba:

opr@toshi$ perl stat.pl
Listo> 19
Listo> 45
Listo> 24
Listo> 15
Listo> 39
Listo> 48
Listo> 36
Listo> count
count = 7
Listo> 10
Listo> 28
Listo> 30
Listo> count
count = 10
Listo> mean
mean = 29.4
Listo> standard_deviation
standard_deviation = 12.685950233756
Listo> salir
Error: tipee 'help' para ayuda
Listo> help
Error: tipee 'help' para ayuda
Listo> exit
opr@toshi$

Una mejora sencilla

Una mejora de estilo podría ser eliminar el if de la línea 15 y hacer la comparación en el given, esto además me permite mostrar que given topicaliza $_ al valor dado y que las clausulas when no solo comparan cadenas (usando eq) y expresiones regulares (usando =~), sino que permiten, entre otros, escribir expresiones booleanas utilizando $_ como un alias al valor dado en el given.

 1 #!/usr/bin/perl
 2 
 3 use Modern::Perl;
 4 use Scalar::Util qw( looks_like_number );
 5 use Statistics::Descriptive;
 6 
 7 use constant SYNTAX_ERROR => "Error: tipee 'help' para ayuda";
 8 
 9 my $s = Statistics::Descriptive::Full->new();
10 while (1) {
11     print "Listo> ";
12     my $command = readline(STDIN) // last;
13     $command =~ s/^\s+//; $command =~ s/\s+$//;
14     given ($command) {
15         when ( looks_like_number($_) ) { $s->add_data($command) }
16         when ("sum")                   { say "$command = " . $s->sum() }
17         when ("mean")                  { say "$command = " . $s->mean() }
18         when ("count")                 { say "$command = " . $s->count() }
19         when ("variance")              { say "$command = " . $s->variance() }
20         when ("standard_deviation")    { say "$command = " . $s->standard_deviation() }
21         when ("min")                   { say "$command = " . $s->min() }
22         when ("mindex")                { say "$command = " . $s->mindex() }
23         when ("max")                   { say "$command = " . $s->max() }
24         when ("maxdex")                { say "$command = " . $s->maxdex() }
25         when ("sample_range")          { say "$command = " . $s->sample_range() }
26         when ("median")                { say "$command = " . $s->median() }
27         when ("harmonic_mean")         { say "$command = " . $s->harmonic_mean() }
28         when ("geometric_mean")        { say "$command = " . $s->geometric_mean() }
29         when ("mode")                  { say "$command = " . $s->mode() }
30         when ("trimmed_mean")          { say "$command = " . $s->trimmed_mean() }
31         when (/^(exit|quit)$/)         {last}
32         default                        { say SYNTAX_ERROR }
33     }
34 }
Pienso que casi cualquier programador habituado a lenguajes dinámicos como Python o Ruby puede comprender sin dificultad código en Perl Moderno y sentirse cómodo trabajando con este.
Los programadores de lenguajes como C, C++,C# o Java, después de adaptarse a algunos conceptos deberían sentir una especie de experiencia liberadora, porque seguramente en cualquiera de ellos cuesta mucho más escribir un programa como este.
En el próximo artículo veremos algunas características dinámicas de Perl, que hacen el programa más corto, flexible y fácil de mantener.

lunes, 24 de agosto de 2009

Perl arcaico

[English translation]
Hace un par de dias me tocó atender un proveedor que vino a ofrecer sus servicios para el desarrollo de una aplicación web.
Como alguno de los participantes de la organización tuvo que manejar un imprevisto aproveché para iniciar un pequeña investigación durante la charla informal: "¿Que herramientas de desarrollo utilizan en su empresa?".
En un mundo ideal la respuesta hubiera sido: "Perl", pero me informaron que ellos trabajan principalmente en Python, aunque pueden trabajar en otros ambientes, incluyendo Perl.
Después de informarles que en la organización preferimos Perl para el desarrollo de nuestras aplicaciones, y después de un micro debate religioso, uno de ellos (Juan) concluyó:
En definitiva cualquier cosa que se puede hacer en un lenguaje se puede hacer en el otro, solo que en Perl la programación es más arcaica
en aquel momento casi me altero, pero en vista de que llegó el que faltaba y que lo importante era la reunión, me quedé tranquilo.
Ahora en retrospectiva me pregunto: ¿a qué se refería con eso de que Perl es arcaico?, tal vez, Juan se refería a Perl 1 (1987), que era una especie de Shell Script con grep, sed y awk incluido, incluso pudiera pensar que dijera eso hasta de Perl 4 (1991, un poco después de Python 1.0), sin embargo la era actual de Perl es 5 (1994) y en vista de la edad del personaje, creo que en realidad Juan no encontró una palabra que describiera todos los míticos defectos de Perl y terminó usando la palabra incorrecta.
Si Perl fuera arcaico, probablemente la programación orientada a objetos y la programación funcional también lo sean, sin embargo son las dos tecnologías con más impulso en la actualidad, y dado que incluso el sistema de orientación a objetos de Perl fue copiado de Python, asumiré que quiso decir alguna de las siguientes:
  1. Perl es feo
  2. Perl es desordenado
  3. Perl es ilegible
  4. Perl es incomprensible
Voy a responder brevemente a estos prejuicios que se han difundido ampliamente por Internet y para los cuales no hay un sustento real, y mucho menos después del renacimiento de Perl (del que hablaré en otro artículo).

Perl es feo

Como eso es cuestión de gustos, lo que es feo para algunos puede ser muy atractivo para otros. Pero suponiendo que Perl es uno de los lenguajes más feos, tiene características que lo hacen un lenguaje único para resolver una gran cantidad de problemas con facilidad.
Una de las características que hacen la sintaxis de Perl recargada (no necesariamente fea) son los sellos (sigils) que indican el tipo de cada variable, sin embargo esto facilita la extensibilidad del lenguaje y permite la interpolación en las cadenas, y cuando me refiero a que Perl es extensible quiero decir que se puede intervenir en el proceso de compilación para cambiar la sintaxis original, una característica de muy alto nivel que comparte con muy pocos lenguajes, y que es el fundamento de los lenguajes de dominio específico (o DSLs por sus siglas en inglés) que son muy útiles y populares. Perl ofrece al menos tres mecanismos diferentes para lograr este objetivo.
Otra característica es la práctica integración de las expresiones regulares en el lenguaje, logrando el uso extensivo de las mismas, lamentablemente dichas expresiones son feas sin importar el lenguaje. Tomemos como ejemplo el reconocimiento de una instrucción específica de LaTeX:

\begin{document}

En Perl se vería algo como:

if ( $latex =~ m/\\begin\{[a-z]+\}/ ) ...

En Python:

patron = re.compile(r'\\begin\{[a-z]+\}')
if
patron.match(latex):
...

En realidad ninguno de los dos es bonito, pero realmente en Perl es más sucinto y hasta más fácil de entender, y eso que no muestro lo tedioso que es usarlas en Java.
Finalmente está el bendito argumento sobre el estilo y todo aquella tontería de que el compilador obliga a indentar correctamente. Lo digo porque aún cuando es cierto que uno termina acostumbrándose, también es cierto que a veces es un fastidio y estorba, y en esos casos no hay remedio. Mas adelante hay un ejemplo de Python que no es fácil de formatear por lo inflexible de la sintaxis.
Cuando se requiere un estilo de código en particular, se usan herramientas de formateo, por ejemplo para C yo uso indent, y para Perl uso perltidy, últimamente el código que escribo suele estar en el siguiente estilo:

perltidy -l=99 -sbl

No importa como me entreguen el código o como lo transcriba yo mismo, porque lo puedo poner en mi estilo estándar con un solo comando, incluso antes de salvarlo (porque además uso vim).

Perl es desordenado

Los lenguajes no son desordenados, la gente lo es.
Sin embargo hay lenguajes que tienen más mecanismos que otros para organizar un proyecto, en particular Perl ofrece varias formas de organizar el código que se adaptan a muchas necesidades, que van desde la programación en una sola línea (de comandos), hasta la construcción de aplicaciones grandes y complejas.
El lenguaje permite la creación de módulos procedimentales con sus propios espacios de nombres, que incluso se pueden organizar en multiples archivos para ser cargados "on demand", bien enterprise, ¿cierto?.
Perl tiene un sistema básico de orientación a objetos que permite implementar OOP de muchas maneras diferentes, porque mientras muchos lenguajes solo hay una forma fija de manejar objetos, en Perl: "hay más de una forma de hacerlo".
Lo que no hace perl es obligar al programador a seguir una estructura determinada, sin importar si eso se adapta a las necesidades de un programa específico, porque si hiciera eso se parecería a Java.

Perl es ilegible

Esto es solo una combinación particular de los dos mitos que le preceden, aunque también se usa el argumento de que no se puede leer el código que se escribío hace 15 min. y eso bajo ciertas circunstancias es bueno, porque permite escribir programas rápidamente aunque sean sucios, después de todo nadie quiere ponerse a diseñar y documentar siguiendo los principios de ingeniería de software para comprender el último programa que escribí hace unas horas, únicamente porque necesitaba una pista sobre la frecuencia de aparición de los caracteres en una docena de archivos:

perl -MYAML -ne '$c{$_}++for split//;END{print Dump\%c}' datos.txt

Es más fácil escribirlo nuevamente que intentar comprenderlo, claro que el programa es una tontería en Perl porque tiene ciertas construcciones mágicas, pero no intenten algo así con otro lenguaje, porque su mejor escenario es lograr que funciona, pero garantizo que va a ser mucho más largo y difícil.
Sin embargo Perl también permite escribir el código mas bonito si eso fuera lo necesario:

use YAML;
use IO::File;
use strict;

my %cuentas;
my $fd = new IO::File $ARGV[0], "r";
while ( my $line = readline($fd) ) {
    for my $letra ( split( //, $line ) ) {
        $cuentas{$letra}++;
    }
}
print YAML::Dump( \%cuentas )
Estoy seguro de que no es muy difícil para un programador de Python o Ruby comprender lo que hace ese código ¿o sí?, luego la cuestión no es que el lenguaje sea ilegible, sino la motivación para escribir el programa y en todo caso la inteligencia del programador para escribir código legible o fácilmente mantenible aún por compañeros poco experimentados.
Finalmente la menor de mis preocupaciones fue el formato del código, pues la mayoría del trabajo lo hace mi editor automáticamente, pero por si acaso también lo pase por perltidy para que vean lo bien formateado que queda.

Perl es incomprensible

¿Para quién?, por ejemplo, el japonés es incomprensible para mi, pero dudo que lo sea para la mayoría de la gente que vive en Tokio, de igual manera Perl es incomprensible para alguien que no este entrenado para comprenderlo. En la sección anterior di un ejemplo de que se puede trabajar en Perl escribiendo el código de manera tan clara como en Python o en Java.
Ciertamente hay mucho código Perl que es prácticamente incomprensible excepto para los gurúes del lenguaje, pero eso no significa que los programas deban escribirse de esa manera.
Lo que si es cierto es que la forma sucinta que permite ofuscar el código hace de perl, no solo un interpretador sino una herramienta de uso común en la línea de comandos y al mismo tiempo permite la expresión del ingenio y maestría en el dominio del lenguaje. Hay muy pocos lenguajes que permitan hacer los JAPHs como Perl.
La falacia aparece cuando se dicen cosas como: "Perl es maligno porque permite esas cosas", "En Python es imposible hacer programas incomprensibles", ¿ah si?, a ver quien entiende este programita en Python:

for n in range(12):
    exec("abcdefghijkl"[n]+"=lambda x=0,y=0: "+filter(
        lambda x:x not in "\n$\r","""(x*y#x/x!range(x,
y#x+y!b(1,1#d(e~,e~#d(f~,f~#c(e~,e~+d(g~,d(g~,g~))#"%4
d" % a(x,y#map(lambda y:i(x,y),h~#" ".join(j(x)#"\\n".
join(map(k,h~))""".replace("~","()").replace("#",")!")
        ).split("!")[n])
print l()
Y puedo buscar cosas mucho más feas en Java, lo que significa que se pueden escribir programas incomprensibles en cualquier lenguaje, y un novato probablemente lo pueda hacer simplemente aprovechando su ignorancia. Por ello la importancia del personal calificado sin importar cual sea el lenguaje.
Sin embargo no se aprovechará todo el potencial de los programadores a menos que el lenguaje provea mecanismos de abstracción del más alto nivel, y es en este caso que lenguajes como Java y PHP son bastante malos mientras Perl supera a Python y a Ruby con holgura, descalificando completamente cualquier posibilidad de catalogar a Perl como un lenguaje arcaico.
Perl puede ser tan corporativo como cualquier otro lenguaje, y dominar este lenguaje en una organización, supone una inversión donde el código se puede utilizar y reutilizar de muchas maneras: desde los administradores de sistemas hasta los desarrolladores, pasando por los administradores de base de datos, etc.; en soluciones que van desde una simple operación de línea de comandos hasta la programación de una gran aplicación corporativa.