In Perl zijn er anonieme subroutines en closures. Anonieme subroutines worden gedeclareerd met sub zonder naam en retourneren een verwijzing naar de code. Een closure is een subroutine die de lexicale scope "vastlegt", inclusief de variabelen die bestonden toen deze werd gemaakt.
Voorbeeld van een anonieme subroutine en closure:
sub make_incrementer { my $inc = shift; return sub { $_[0] + $inc }; } my $inc10 = make_incrementer(10); print $inc10->(5); # Geeft 15 terug
Closures worden actief gebruikt voor het maken van functie-fabrieken, generatoren en callbacks (bijvoorbeeld in map/grep, gebeurtenishandlers).
Een belangrijk punt: als een closure verwijst naar variabelen die op hun beurt naar de closure zelf verwijzen (rechtstreeks of via een structuur), ontstaat er een cyclische verwijzing en kan er een geheugenlek optreden.
Wanneer worden de variabelen, gesloten in een closure, vrijgegeven? Is er een verschil in gedrag voor my en our?
Antwoord: De my variabelen, gesloten binnen een closure, blijven leven zolang er tenminste één verwijzing naar de closure zelf bestaat. Ze worden niet vrijgegeven na de voltooiing van de functie, hun levensduur is de gehele levensduur van de closure. Voor our variabelen is er dit gedrag niet — ze zijn toegankelijk voor alle closures en worden vrijgegeven na de voltooiing van het programma. Als je vergeet de closure te verwijderen, kan dat leiden tot geheugenlekken.
Voorbeeld van een probleem:
my $cref; { my $val = 5; $cref = sub { $val++ }; } # $val bestaat nog steeds zolang $cref bestaat
Verhaal
In een serverapplicatie werd voor elke gebruiker een closure-context voor de callback aangemaakt. Maar de closure verwees ook naar de user-structuur, wat leidde tot een cyclische verwijzing. Hierdoor verwijderde de garbage collector de gebruikersobjecten niet, zelfs niet na uitloggen — geheugenlekken groeiden exponentieel.
Verhaal
In een daemon-applicatie voor achtergrondverwerking van gebeurtenissen werden closures met gesloten teller-variabelen gebruikt, maar ze vergaten de arrays te legen waar ze naar verwezen. Resultaat — door een paar vergeten closures werden oude berichtgegevens per ongeluk verzameld, tot de daemon moest worden handmatig opgeruimd.
Verhaal
Een ontwikkelaar probeerde een our-variabele in een closure te gebruiken, in de veronderstelling dat een "gesloten" gedrag zou optreden — maar alle closures deelden één variabele, wat leidde tot race condities tijdens parallelle uitvoering. De bug manifesteerde zich bij gelijktijdig werken van gebruikers met verschillende parameters (onjuiste berekeningen).