In the below Perl 5 code, I refactored to pull the two halves of the PID
file handling out of init_server(), but to do so, I had to return a sub
from pid_file_handler() that acted as a "continuation". The syntax is a
bit ugly, though. Is there a cleaner way to this in Perl 6?
######
sub init_server {
my %options = @_;
# ...
# Do top (pre-daemonize) portion of PID file handling.
my $handler = pid_file_handler($options{pid_file});
# Detach from parent session and get to clean state.
become_daemon();
# Do bottom (post-daemonize) portion of PID file handling.
$handler->();
# ...
}
sub pid_file_handler {
# Do top half (pre-daemonize) PID file handling ...
my $filename = shift;
my $basename = lc $BRAND;
my $PID_FILE = $filename || "$PID_FILE_DIR/$basename.pid";
my $pid_file = open_pid_file($PID_FILE);
# ... and return a "continuation" on the bottom half (post-daemonize).
return sub {
$MASTER_PID = $$;
print $pid_file $$;
close $pid_file;
};
}
######
When I asked this question on #perl6, pmurias suggested using
gather/take syntax, but that didn't feel right to me either -- it's
contrived in a similar way to using a one-off closure.
pmichaud offered several possibilities (I've converted some of his
suggestions expressed as prose into code, so the errors there are mine):
1. Take advantage of Perl 6 syntax reduction to turn 'return sub {...}'
into 'return {...}' (or even just fall of the end with '{...}', I
suppose). This is visually slightly better, but still leaves the
bottom half inside a block that merely exists to satisfy Perl, not
actually representing anything intrinsic about the problem.
2. Throw a resumable exception in the middle:
sub init_server {
# ...
pid_file_handler($options{pid_file});
become_daemon();
pid_file_handler();
# ...
}
sub pid_file_handler {
# ... top half ...
throw ResumableException;
# ... bottom half ...
}
He also suggested a variant syntax with an adverb on return:
sub pid_file_handler {
# ... top half ...
return :resumable;
# ... bottom half ...
}
I suggested a naked yield syntax:
sub pid_file_handler {
# ... top half ...
yield;
# ... bottom half ...
}
These all desugar to the same thing, of course.
3. Make become_daemon a part of pid_file_handler, or vice-versa.
I rejected both of these on the basis of separating different
things into different subs. The two tasks are only tangentially
related, and neither really seems like a subordinate op of the
other.
4. In order to keep the sub separate, but still not split the
pid_file_handler call, I came up with a variation of #3 in which
pid_file_handler takes a callback parameter:
sub init_server {
# ...
pid_file_handler($options{pid_file}, &become_daemon);
# ...
}
sub pid_file_handler($pid_file, &callback) {
# ... top half ...
callback();
# ... bottom half ...
}
That seems like a silly contortion to hide the problem, and
doesn't represent my intent well -- the pid file handler doesn't
need to send a message, it needs to yield control while waiting
for something else to happen.
5. Make a new PidHandler class and address the problem in OO fashion:
sub init_server {
# ...
my $pid_handler = PidHandler.new(file => $options{pid_file});
$pid_handler.top();
become_daemon();
$pid_handler.bottom();
#...
}
This is certainly workable, but again feels like a contrived
workaround in the same way that gather/take and return {...} do.
Plus, writing a new class and using OO/method call syntax just to
allow a sub to be "split" seems like pointless busy work. Not
as bad in Perl 6 as in Perl 5, but still.
In the end, I think I like the 'naked yield' idea best of the ones we
have so far. Any comments or other ideas? [1]
-'f
[1] Other than that I've used the word 'contrived' too many times. :-)