Improv has asked for the wisdom of the Perl Monks concerning the following question:

Let's say we have a module that handles structured argument passing, so that calling code can say
$foo->bar(myarg1 => "myval", myarg2 => "baz");
and recieving code does something like
sub bar { my $self = shift @_; my $args = Argpass::new('strictpolicy', @_); my $argone = $args->accept('myarg1', 'defaultvalue'); my $argtwo = $args->mandate('myarg2'); $args->argok(); # And the function goes on, provided all mandated arguments are passed + in and any arguments that needed type checking passed it successfull +y, anything that needed processing was processed, etc.. # ... # ... }

(fill in a number of additional possibly meaningful and useful ways to automagically pass arguments around, some mandatory, some not, some with defaults, include some typechecking, etc)

This works, and it's been used on some reasonably large projects. It would be useful to add a new 'policy' allowing me to do a kind of reflection - to force the caller, as soon as argument passing is finished (when argok() is called) to return with a structured set of data describing the parameters it takes based on all these accept/mandate/etc methods. Unfortunately, after staring at caller(), goto, etc, I don't see a way to do this.

In short, is there a way, from a subroutine/method to force your caller to return with a value you specify? (let's assume we don't want to complicate the caller with conditionals and have it do the returning)

Replies are listed 'Best First'.
Re: Force caller to return - reflection
by kyle (Abbot) on Jan 20, 2009 at 03:06 UTC

    The first thing that comes to my mind is to have argok throw an exception. With Exception::Class, you can store any arbitrary data you want in the exception you throw. The problem is that bar's caller has to know to catch it. It doesn't just come as a return value.

      That's a bit more heavyweight in syntax than I'd like (but maybe I could wrap it up so as not to expose it to the user). I'll look into it - thanks.
Re: Force caller to return - reflection
by Corion (Patriarch) on Jan 20, 2009 at 10:53 UTC

    While I'm not exactly sure that what you want to do is sane, I think that Scope::Upper can help you, as it claims that you can unwind the stack frame and return arbitrary values there.

      What I want to do isn't sane in the general case, but it is sane for the specific case of how the module works. :) Scope::Upper looks like it's designed to manipulate scoping rules - unwind() doesn't actually impact program flow. What I'm looking for is something a bit more like "pretend you're doing tail recursion and pop the call stack twice rather than once, returning this value". Thanks for the idea though.
        unwind() doesn't actually impact program flow
        Oh yes it does. Just how do you think you can return values from an upper scope without restarting from there? Not mentioning that that would be of very limited use if it didn't!

        Continuation::Escape gives an alternate interface to it, which effectively relieves you from having to specify the target context yourself. But OTOH it requires you to be able to "mark" the context you want to return to. Reading its source code will surely teach you how it builds up on unwind.
Re: Force caller to return - reflection
by moritz (Cardinal) on Jan 20, 2009 at 07:09 UTC
    kyle is absolutely right - for returning through multiple subroutine scopes exceptions are the weapon of choice.

    And since a failed argument checking actually represents an error, I don't see why you should not simply die (or confess, croak, whatever).

Re: Force caller to return - reflection
by JavaFan (Canon) on Jan 20, 2009 at 10:49 UTC
    The answer is no. Even throwing an exception from argok() can be easily defeated: the called subroutine has first dibs on catching exceptions.

    But since you are in control of argok(), can't you just collect the information you want in argok()?

      The information is collected in argok(). The information collected there should be returned to the caller of the method/subroutine that uses Argpass, not the user of those methods/subroutines. I would also prefer not to muddy the user code with conditional returns, and ideally to avoid using exceptions. I'm not completely surprised that this isn't possible though.