On Fri, 2009-01-02 at 22:56 +0100, Aristotle Pagaltzis wrote: > > 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. > > Contrived how?
Meaning, the gather/take syntax doesn't make much sense, because we're not "gathering" anything; the PID file handler has nothing to return. We'd only be using it for the "side effect" of being able to pause the callee's execution and resume it later. > When you have an explicit entity representing the continuation, > all of these questions resolve themselves in at once: all calls > to the original routine create a new continuation, and all calls > via the state object are resumptions. There is no ambiguity or > subtlety to think about. I like this argument. I'm not sure it's applicable in every case, but it certainly applies to the class of situations containing my problem. > So from the perspective of the caller, I consider the “one-off” > closure ideal: the first call yields an object that can be used > to resume the call. > > However, I agree that having to use an extra block inside the > routine and return it explicity is suboptimal. It would be nice > if there was a `yield` keyword that not only threw a resumable > exception, but also closed over the exception object in a > function that, when called, resumes the original function. > > That way, you get this combination: > > sub pid_file_handler ( $filename ) { > # ... top half ... > yield; > # ... bottom half ... > } > > sub init_server { > # ... > my $write_pid = pid_file_handler( $options<pid_file> ); > become_daemon(); > $write_pid(); > # ... > } That's pretty nice. Perhaps we can make it even cleaner with a few small tweaks to init_server(): sub init_server(:$pid_file, ...) { # ... my &write_pid := pid_file_handler($pid_file); become_daemon(); write_pid(); # ... } So far, this variant is winning for me, I think. It's slightly more verbose on the caller's side than the yield variant I had proposed, but it's also more explicit, and allows (as you said) a clean syntactic separation between starting the PID file handler and continuing it. It does bring up a question, though. What if pid_file_handler() needed to be broken into three or more pieces, thus containing multiple yield statements? Does only the first one return a continuation object, which can be called repeatedly to continue after each yield like this? sub init_server(:$pid_file, ...) { # ... my &more_pid_stuff := pid_file_handler($pid_file); become_daemon(); more_pid_stuff(); do_something(); more_pid_stuff(); do_something_else(); more_pid_stuff(); # ... } Or does each yield produce a fresh new continuation object like this? sub init_server(:$pid_file, ...) { # ... my &write_pid := pid_file_handler($pid_file); become_daemon(); my &fold_pid := write_pid(); do_something(); my &spindle_pid := fold_pid(); do_something_else(); spindle_pid(); # ... } (Note that I assume you can simply ignore the returned object if you don't plan to continue the operation any more, without raising a warning.) Certainly the first version has less visual clutter, so I tend to lean that way by default. But the second design would allow one to create a tree of partial executions, by calling any earlier continuation object again. That's a very powerful concept that I don't want to give up on. Supporting both feels like it might be an adverb on the invocation (possibly with a frosty sugar coating available). It would be nice to support invoking a continuation in "ratcheting" and "forgetful" modes. Thoughts? -'f