[l-desarrollo] Mejores prácticas de use y require (Perl)

18 views
Skip to first unread message

Alberto Mijares

unread,
Mar 26, 2012, 8:56:46 PM3/26/12
to Aplicaciones y Desarrollo en Linux
Hola lista,

Se me presentan algunas dudas:

1.- Ya he leído acerca de las diferencias teóricas de use y require.
Está documentado que _use_ evalúa en tiempo de compilación y _require_
en tiempo de ejecución; sin embargo, no me queda claro qué beneficio
puede representar cada uno (lack of knowledge, perhaps). En otras
palabras, ¿cuándo es conveniente usar _require_?

2.- Cuando estamos escribiendo nuestros propios módulos (digamos que
son de consumo interno y hacemos algo quick and dirty, no para CPAN),
y estos a su vez utilizan otros módulos, ¿hay algún beneficio en
cuanto optimización o estilo cuando se cargan los módulos necesarios
dentro de los métodos de la clase? ¿O es mejor simplemente cargar todo
lo que vas a usar al inicio?

Gracias de antemano.

Saludos


Alberto Mijares
_______________________________________________
l-desarrollo mailing list
l-desa...@velug.org.ve
http://listas.velug.org.ve/mailman/listinfo/l-desarrollo

Alejandro Imass

unread,
Mar 27, 2012, 7:33:38 AM3/27/12
to Aplicaciones y Desarrollo en Linux
On Mon, Mar 26, 2012 at 8:56 PM, Alberto Mijares <amij...@gmail.com> wrote:
> Hola lista,
>
> Se me presentan algunas dudas:
>
> 1.- Ya he leído acerca de las diferencias teóricas de use y require.
> Está documentado que _use_ evalúa en tiempo de compilación y _require_
> en tiempo de ejecución; sin embargo, no me queda claro qué beneficio
> puede representar cada uno (lack of knowledge, perhaps). En otras
> palabras, ¿cuándo es conveniente usar _require_?
>
> 2.- Cuando estamos escribiendo nuestros propios módulos (digamos que
> son de consumo interno y hacemos algo quick and dirty, no para CPAN),
> y estos a su vez utilizan otros módulos, ¿hay algún beneficio en
> cuanto optimización o estilo cuando se cargan los módulos necesarios
> dentro de los métodos de la clase? ¿O es mejor simplemente cargar todo
> lo que vas a usar al inicio?
>

use realmente usar require y luego ejecuta el import de símbolos que
hayan sido exportados por el package
require solo hace el eval pero entiendo que no hace el import
por lo tanto usa use siempre

Los packages en Perl son generalmente de 2 tipos: orientado a
funciones u orientado a objetos
Lo paquetes pueden o no exportar símbolos al código que ejecuta el use
or el require. Eso depende mucho del autor y el tipo de paquete. En
otras palabras hay paquetes que tiene sentido exportar símbolos por
default y otros no. Los paquetes 100% orientados a objetos
generalmente no exportan nada.

En definitiva usa use y olvídate de require a menos que sepas
exactamente para que lo necesitas.

Con respecto a que si se hace en compile time o no, realmente es lo
mismo porque ambos ejecutan un eval completo sobre el paquete
importado, así que es casi lo mismo. De hecho use es _exactamente_
equivalente a:

BEGIN { require Module; Module->import( LIST ); }

Con la diferencia (creo) que garantiza que se hace ANTES que tu código
por el BEGIN. En cambio con el require _creo_ que no hay tal garantía
pero realmente no estoy seguro. En fin, usa use y ya.

Saludos,

--
Alejandro

Alberto Mijares

unread,
Mar 27, 2012, 10:41:38 AM3/27/12
to Aplicaciones y Desarrollo en Linux
> pero realmente no estoy seguro. En fin, usa use y ya.
>


Entendido. Gracias. Con respecto a si es más conveniente el _use_
dentro de la definición de los métodos/funciones o no, ¿alguna
opinión?

Para ser más explícito:

Digamos que creo una clase con dos métodos y cada método se apoya en
un módulo distinto. ¿Hay alguna diferencia o recomendación en cuanto a
las siguientes versiones?


Versión 1:

package Class;

sub x {
use ModuleA;
[...]
}
sub y {
use ModuleB;
[...]
}
1;


Versión 2:

package Class;

use ModuleA;
use ModuleB;

sub x {
[...]
}
sub y {
[...]
}
1;


Disculpen si es una pregunta rebuscada y sin sentido pero por alguna
razón tengo esa inquietud y no logro darme respuesta con la literatura
que he conseguido.

Ernesto Hernández-Novich

unread,
Mar 28, 2012, 8:03:35 PM3/28/12
to Aplicaciones y Desarrollo en Linux
On Mon, 2012-03-26 at 20:26 -0430, Alberto Mijares wrote:
> 1.- Ya he leído acerca de las diferencias teóricas de use y require.

Las diferencias son *prácticas* :)

> Está documentado que _use_ evalúa en tiempo de compilación y _require_
> en tiempo de ejecución; sin embargo, no me queda claro qué beneficio
> puede representar cada uno (lack of knowledge, perhaps). En otras
> palabras, ¿cuándo es conveniente usar _require_?

Como ya han apuntado otros, 'use' opera a tiempo de compilación y lo que
hace es agregar un bloque BEGIN de manera que sea lo *primero* que se
ejecute después de la fase de reconocimiento sintáctico justo antes de
la fase de análisis de contexto, de manera que todos los símbolos del
módulo importado estén disponibles para el mencionado análisis.

En contraste, un 'require' fuera de un bloque BEGIN solamente es
efectivo a tiempo de ejecución, por lo que los símbolos del módulo
importado *no* están disponibles a tiempo de compilación.

Considera

use Foo;
my $foo = Foo->new();

ese programa no compila a menos que se encuentre la librería 'Foo' en el
conjunto de directorios de búsqueda; no compila porque el 'Foo' en
'Foo->new()' sería un símbolo desconocido (bareword). Entonces, el uso
de 'use' permite *garantizar* que al compilar el programa, todos los
módulos requeridos están allí (posiblemente con las versiones
específicas solicitadas como argumentos a 'use').

Ahora considera

require Foo;
my $foo = Foo->new();

ese programa compila, pero al pasar a la fase de ejecución podría emiti
un error indicando que no encuentra la librería, si es que esta no
estuviera disponible. El comportamiento exhibido es el mismo (error
indicando que "no encuentro Foo") pero la detección ocurre en momentos
diferentes.

Imagina que estás escribiendo un módulo que puede usar *cualquiera* de
tres módulos auxiliares Foo, Bar o Baz. Puede usar uno cualquiera de los
tres, e incluso tu como autor prefieres Foo antes que Bar antes que Baz.
Si tu librería comienza con

use Foo;
use Bar;
use Baz;

estás siendo excesivamente abusivo con tu usuario: ¿por qué obligarlo a
tener los tres, si basta uno cualquiera? Aquí es donde 'require' resulta
útil aplicando una construcción como

BEGIN {
my($f, @ms, $m);
$f = 0;
@ms = qw(Foo Bar Baz);
foreach my $m (@ms) {
if (eval q{ require $m" } ) {
$m->import(); # Sólo si hace falta
$f = 1;
last;
}
}
die "Necesitas alguno de @ms" unless $f;
}

que deja a tiempo de ejecución el problema de encontrar *alguno* de los
tres módulos. Nota el "sólo si hace falta" en el import() porque depende
de cómo hayan sido escritos Foo, Bar y Baz (API funcional vs. API OO).

Pero yo no lo haría así; ese código es solamente para ilustrar la
utilidad. Module::Runtime o Module::Load son soluciones mejores para
este problema, que en el fondo hacen lo mismo, pero de manera controlada
para que no reinventes la rueda cuadrada y montes un rancho al mejor
estilo de esos otros lenguajes menos afortunados en diseño y comunidad.

Si sigues investigando ese camino, eventualmente vas a encontrar 'use
autouse'. Ese no es el módulo que estás buscando, déjalo ir.

Pero ahora me dirás que ponerlo en un bloque BEGIN no es siempre
conveniente, porque como ese bloque es lo primero que se ejecuta, quizás
tu módulo o programa aún no "sabe lo suficiente" como para decidir si le
conviene Foo, Bar, Baz o alguna otra cosa. Entonces, es particularmente
interesante la librería Module::Load::Conditional (que está en core en
cualquier Perl moderno) que ofrece mecanismos para establecer si
determinado módulo está disponible en alguna versión precisa, y cargarlo
a tiempo de ejecución.

Por supuesto que esto está muy cerca de permitir un sistema de plugins
dependiente de una configuración y librerías disponibles en el sistema,
y si logré mi cometido con la explicación, cualquier lector astuto está
pensando en escribir

package Do::Magic;

BEGIN {
... buscar el módulo Foo, o Bar, o Baz ...
... buscar *todos* los módulos por debajo de Do::Magic ...
}

porque uno aspira que los programadores escriban módulos "plugin"

Do::Magic::WithCards
Do::Magic::WithMoney
Do::Magic::WithFire
...

y que en tu código puedas cargar los que haya para usarlos libremente.
Pero volvemos a que esto es Perl, así que la librería Module::Pluggable
te permite escribir

package Do::Magic;
use Module::Pluggable;
... parte común de todos los plugins ...

y en un módulo usuario o en tu programa principal

use Do::Magic;
my $magic = Do::Magic->new();
my @plugins = $magic->plugins();

En efecto, Module::Pluggable se encargó de buscar todas las librerías
Do::Magic::* en tu sistema, te ofrece un montón de métodos para que
hagas introspección de los módulos encontrados para que los cargues y
apliques.

El núcleo del lenguaje Perl sólo provee 'eval', 'require' e
introspección completa; los módulos que describí fueron escritos por
programadores que saben diferenciar entre compilación, ejecución y
aprovechar la introspección. No es una idea nueva (¡hi Schemers!) pero
es sorprendente como muchos lenguajes dinámicos están en pañales en este
sentido.

En un lenguaje más estático, este tipo de cosas no son posibles en
general, y esquemas de métodos virtuales con una violación horrenda del
sistema de tipos usando cabillas de XML embadurnadas con concatenación
de strings llegan bastante cerca... pero dejan el sistema de tipos "como
boca de payaso". Lo triste no es que los programadores lo hagan, lo
triste es que el sistema de tipos se deje... poco serio :)

> 2.- Cuando estamos escribiendo nuestros propios módulos (digamos que
> son de consumo interno y hacemos algo quick and dirty, no para CPAN),
> y estos a su vez utilizan otros módulos, ¿hay algún beneficio en
> cuanto optimización o estilo cuando se cargan los módulos necesarios
> dentro de los métodos de la clase? ¿O es mejor simplemente cargar todo
> lo que vas a usar al inicio?

Si utilizas 'use', no importa *dónde* hayas puesto la línea, siempre se
van a cargar a tiempo de compilación. Lo que va a cambiar es el
*alcance* de los nombres importados

{
...
{
use Foo;
... los nombres de Foo son alcanzables aquí ...
}
... los nombres de Foo no son alcanzables aquí
}

así que para evitar confusiones, pon todos tus 'use' al principio.
--
Ernesto Hernández-Novich - @iamemhn - Unix: Live free or die!
Geek by nature, Linux by choice, Debian of course.
If you can't aptitude it, it isn't useful or doesn't exist.
GPG Key Fingerprint = 438C 49A2 A8C7 E7D7 1500 C507 96D6 A3D6 2F4C 85E3

Reply all
Reply to author
Forward
0 new messages