Emulating Ruby Blocks in Perl
Ruby Blocks
Ruby blocks allow you to run code in the caller from within a method:
# definition def twice yield 1 yield 2 end # example call twice do |n| puts "croak! (#{n})" end # output # croak! (1) # croak! (2)
In case you never saw something like that before, twice expects to be called with a block. That's the code between the do ... end keywords in the example call. Each call to yield runs the block in the caller. Arguments may be passed, in the example we pass a integer.
This is a powerful tool, Ruby is full of all sorts of internal iterators thanks to it:
array.each_with_index do |item, i| # ... end
You can wrap/decorate chunks of code with them providing a very easy and intuitive usage. For example:
File.open(filename) do |file| # ... end
opens a file, passes the filehandle to the block, and when the block is done it closes it.
Another example from my Rails Contributors application (simplified here):
acquiring_sync_file do # ... end
The code within the block runs only if acquiring_sync_file could get a lock on some given sync file. If it could, once the block is done the lock is released.
Ruby Blocks in Perl
Perl does not have blocks like that, but you can emulate them in a way that may cover some use cases:
acquiring_sync_file { # ... };
The trick comes from a feature of Perl that is not widely used: subroutine prototypes.
You may have noticed that some builtin Perl subroutines behave in a strange way. For example push:
push @array, 5;
If push was an ordinary subroutine, it would receive the elements of @array and a 5 in @_, with no possible way to modify @array, which was lost by its evaluation in list context before the function call.
But push somehow is able to tell @array from the 5, and actually modify it. There's something special going on there. You can write you own subroutines like push and other thanks to subroutine prototypes. They are documented in perlsub.
Point is: if the first argument of a call is an anonymous subroutine, Perl forgives you the sub keyword if you set an appropriate prototype:
sub twice(&) { my $coderef = shift; $coderef->(1); $coderef->(2); } # as if it said twice sub { ... } twice { my $i = shift; print "croak! ($i)\n"; };
The rationale for that is to give you a way to write your own subroutines with map-like syntax. As you see, we can pass arguments as well.
Opening a file with automatic close would be:
sub xopen(&@) { ... } xopen { my $fh = shift; # ... } $mode, $filename;
As you see xopen arguments would go after the anonymous subroutine, because this trick is only allowed if the coderef is the very first argument.
Drawbacks
The whole range of subroutine prototypes have a few gotchas, but what's specifically needed here works fine and some Ruby idioms can be emulated quite well. Subroutine prototypes do not work at all in methods though.
rubyisms
Simon Cozens' rubyisms Perl module provides a hackish yield so that you do not need to execute coderefs explicitly.