Possible Duplicate:
Is it possible for a Perl subroutine to force its caller to return?
I want to write a subroutine which causes the caller to return under certain conditions. This is meant to be used as a shortcut for validating input to a function. What I have so far is:
sub needs($$) {
my ($condition, $message) = @_;
if (not $condition) {
print "$message\n";
# would like to return from the *parent* here
}
return $condition;
}
sub run_find {
my $arg = shift @_;
needs $arg, "arg required" or return;
needs exists $lang{$arg}, "No such language: $arg" or return;
# etc.
}
The advantage of returning from the caller in needs
would then be to avoid having to write the repetitive or return
inside run_find
and similar functions.
I think you're focussing on the wrong thing here. I do this sort of thing with Data::Constraint, Brick, etc. and talk about this in Mastering Perl. With a little cleverness and thought about the structure of your program and the dynamic features that Perl has, you don't need such a regimented, procedural approach.
However, the first thing you need to figure out is what you really want to know in that calling subroutine. If you just want to know yes or no, it's pretty easy.
The problem with your
needs
is that you're thinking about calling it once for every condition, which forces you to useneeds
to control program flow. That's the wrong way to go.needs
is only there to give you an answer. It's job is not to change program state. It becomes much less useful if you misuse it because some other calling subroutine might want to continue even ifneeds
returns false. Call it once and let it return once. The calling subroutine uses the return value to decide what it should do.The basic structure involves a table that you pass to
needs
. This is your validation profile.You construct your table for whatever your requirements are. In
needs
you just process the table:Now, the really cool thing with this approach is that you don't have to know the table ahead of time. You can pull that from configuration or some other method. That also means that you can change the table dynamically. Now your code shrinks quite a bit:
You cna keep going with this. In Mastering Perl I show a couple of solutions that completely remove that from the code and moves it into a configuration file. That is, you can change the business rules without changing the code.
Remember, almost any time that you are typing the same sequence of characters, you're probably doing it wrong. :)
You may want to look at a similar recent question by kinopiko: Is it possible for a Perl subroutine to force its caller to return?
The executive summary for that is: best solution is to use exceptions (die/eval, Try::Tiny, etc...). You van also use GOTO and possibly Continuation::Escape
Sounds like you are re-inventing exception handling.
The
needs
function should not magically deduce its parent and interrupt the parent's control flow - that's bad manners. What if you add additional functions to the call chain, and you need to go back two or even three functions back? How can you determine this programmatically? Will the caller be expecting his or her function to return early? You should follow the principle of least surprise if you want to avoid bugs - and that means using exceptions to indicate that there is a problem, and having the caller decide how to deal with it:Any code inside of the
try
block is special: if something dies, the exception is caught and stored in$@
. In this case, thecatch
block is executed, which prints the error to STDOUT and control flow continues as normal.Disclaimer: exception handling in Perl is a pain. I recommend Try::Tiny, which protects against many common gotchas (and provides familiar try/catch semantics) and Exception::Class to quickly make exception objects so you can distinguish between Perl's errors and your own.
For validation of arguments, you might find it easier to use a CPAN module such as Params::Validate.
It doesn't make sense to do things this way; ironically, ya doesn't needs
needs
.Here's why.
run_find
is poorly written. If your first condition is true, you'll never test the second one since you'll havereturn
ed already.warn
anddie
functions will provide you printing and/or exiting behavior anyway.Here's how I would write your
run_find
sub if you wanted to terminate execution if your argument fails (renamed it towell_defined
):There should be a way toreturn 0
andwarn
at the same time, but I'll need to play around with it a little more.run_find
can also be written toreturn 0
and the appropriatewarn
message if conditions are not met, andreturn 1
if they are (renamed towell_defined
).This enables Boolean-esque behavior, as demonstrated below: