En Perl se admiten subprogramas anónimos y cierres. Los subprogramas anónimos se declaran con sub sin nombre y devuelven una referencia al código. Un cierre es un subprograma que "captura" el ámbito léxico, incluidas las variables que existían en el momento de su creación.
Ejemplo de subprograma anónimo y closure:
sub make_incrementer { my $inc = shift; return sub { $_[0] + $inc }; } my $inc10 = make_incrementer(10); print $inc10->(5); # Imprimirá 15
Los closures se utilizan activamente para crear fábricas de funciones, generadores y callbacks (por ejemplo, en map/grep, controladores de eventos).
Un punto importante: si un closure se refiere a variables que, a su vez, apuntan al propio closure (directa o indirectamente a través de una estructura), surge una referencia cíclica y puede haber una fuga de memoria.
¿Cuándo se liberan las variables cerradas en un closure? ¿Hay alguna diferencia de comportamiento entre my y our?
Respuesta: Las variables my cerradas dentro de un closure permanecen vivas mientras exista al menos una referencia al propio closure. No se liberan al finalizar la función; su tiempo de vida es todo el tiempo de vida del closure. Para las variables our, no existe tal comportamiento: están disponibles para todos los closures y se liberan al finalizar el programa. Al olvidar eliminar un closure, se pueden provocar fugas de memoria.
Ejemplo del problema:
my $cref; { my $val = 5; $cref = sub { $val++ }; } # $val sigue existiendo mientras $cref esté vivo
Historia
En una aplicación del servidor se creaba un contexto de cierre para cada usuario en el callback. Pero el closure también hacía referencia a la estructura del usuario, lo que llevaba a una referencia cíclica. Como resultado, el recolector de basura no limpiaba los objetos de usuario incluso después del logout, lo que provocaba fugas de memoria que crecían exponencialmente.
Historia
En una aplicación demonio para el procesamiento en segundo plano de eventos, se utilizaban closures con variables cerradas contadoras, pero se olvidaron de reiniciar los arrays a los que hacían referencia. El resultado — debido a algunos closures olvidados, se acumulaban accidentalmente antiguos datos de mensajes, hasta que se obligó a realizar una limpieza manual de la memoria antes de que el demonio fallara.
Historia
Un desarrollador intentó usar una variable our en un closure, esperando un comportamiento "cerrado" — pero todos los closures compartían una sola variable, lo que provocaba condiciones de carrera durante la ejecución paralela. El bug se manifestó cuando los usuarios trabajaban simultáneamente con diferentes parámetros (cálculos erróneos).