Perl defer blocks

Perl recently (well, not so recently), introduced a new experimental feature called defer blocks.
A defer block is just a block that is executed as a last resort when the lexical scope of the containing block expires. To some extent, it is a phaser block that is run at the very last time.
A simple example can prove its usage:

use feature 'defer';
no warnings;

my $sub = sub {
    defer { say "... and see you later!" }

    say "Hello $_[0]"  for ( 1 .. 3 );

};


$sub->( q/Luca/ );




In the above code, the subroutine declares a defer block that will run at the very end of the scope of the subroutine block itself. Once the subroutine is called, its body block is executed, as usual. Once the subroutine is going to complete, and thus expiring its lexical scope, the defer block is executed. Therefore, the output is:

Hello Luca
Hello Luca
Hello Luca
... and see you later!



Clearly, this implies that the defer block is executed as soon as the code block exits, no matter why it is exiting:

use feature 'defer';
no warnings;

my $sub = sub {
    defer { say "... and see you later!" };

    for ( 1 .. 5 ) {
		say "Hello $_[0]";
		return if $_ % 2 == 0;
    }

};


$sub->( q/Luca/ );



In the above, the function returns after a couple of iterations, not concluding the for loop, but the defer block is executed at last as expected:

Hello Luca
Hello Luca
... and see you later!



After all, the rule is simple: a defer block is always executed at last, even if an exception occurs:

use feature 'defer';
no warnings;

my $sub = sub {
    defer { say "... and see you later!" };

    for ( 1 .. 5 ) {
	say "Hello $_[0]";
	die "Argh!";
    }

};


$sub->( q/Luca/ );




The above code produces the following output:

Hello Luca
Argh! at /home/luca/tmp/test.pl line 13.
... and see you later!



defer or continue ?

Resist to the temptation of using defer as a phaser in loops, since this is not going to produce what you want (or maybe it will produce it, depending on what you want, ehm):

for ( 1 .. 3 ) {
    say "Counting $_";
    defer { say "At last the defer block!" }
}



will produce:

Counting 1
At last the defer block!
Counting 2
At last the defer block!
Counting 3
At last the defer block!



In fact, since every time the loop completes an iteration its code block goes out of scope, the defer block is executed at every iteration. Therefore, it looks like defer behaves exactly as a conitnue block, but there is an important difference: being defined into the scope of the containing block, the defer block has access to lexical variables, while the continue block does not!

Conclusions

defer is another piece of art to make great Perl programs!
It allows to define a finally-like behavior even when no exceptions are in charge, and can be used similarly to a continue block within loops, but with some extra powers!

The article Perl defer blocks has been posted by Luca Ferrari on July 20, 2023

Tags: perl