Históricamente, Perl ha soportado el ámbito léxico de las variables, lo que permite el uso de closures — funciones que mantienen el entorno externo. El problema surge cuando un closure hace referencia a variables fuera de su ámbito o cuando las estructuras anidadas crean referencias cíclicas, lo que resulta en fugas de memoria debido a un manejo descuidado de las referencias.
La solución es utilizar closures para crear fábricas de funciones y un estilo funcional, y al mismo tiempo recordar la correcta gestión de referencias al cerrar variables desde un alcance externo.
Ejemplo de código:
sub make_counter { my $count = 0; return sub { $count++; }; } my $counter = make_counter(); print $counter->(), "\n"; # 0 print $counter->(), "\n"; # 1
Características clave:
¿Qué sucederá si se devuelve una función anónima que hace referencia a sí misma?
Se creará una referencia cíclica que Perl no podrá recolectar automáticamente con el recolector de basura. Esto llevará a una fuga de memoria. Para solucionar esto, utiliza referencias débiles, el módulo Scalar::Util:
use Scalar::Util qw(weaken); my $foo; $foo = sub { ... $foo ... }; weaken($foo);
¿Siempre captura un closure una “copia” de la variable, o es una referencia a la misma variable?
Un closure siempre opera sobre la variable actual, su ámbito se fija en el momento de la creación del closure. Así, la variable es la misma para todas las llamadas a la función closure.
¿Es posible hacer que un closure funcione con un estado mutable externo, pero sin tener una referencia fuerte a él?
Sí, utiliza referencias débiles (Scalar::Util::weaken) o estructura el código de forma que las referencias sean mantenidas solo donde sea necesario (por ejemplo, pasa datos desde fuera en cada llamada al closure).
Crearon un callback-closure que cierra $self de un objeto OO, y se mantiene dentro de un hash de callbacks. Después de destruir el objeto, la memoria no se libera.
Pros:
Contras:
El closure hace referencia débilmente a $self correctamente con Scalar::Util::weaken:
use Scalar::Util qw(weaken); my $cb = sub { my $self = shift; weaken($self); ... };
Pros:
Contras: