This thought came out of a question I've been puzzling about in my tiny-brained way, and which I mentioned in the CB yesterday. As well as raising my present topic I'm glad to have an excuse to put down the solution to the problem I discussed, just so anyone who was kind enough to offer advice can see that it didn't fall on deaf ears. Thanks again, sibling monks.
I'd also like to crave your indulgence for any foolishness I may commit in the following, as these questions of scope and naming are new to me and I find them quite difficult.
So, my thought. I think it might be nice if there was a way to make a subroutine behave, in regard to naming scopes, exactly the same as if, instead of calling the module, you had cut and pasted the lines of code into your script in place of the subroutine call.
a way to do this. If so, I'd love to know of it. Maybe you think it wd be a dangerous thing. But, whilst being open to persuasion, I don't agree: the variables are still contained within a lexical scope, only it's the lexical scope of the surrounding block. So if the subroutine creates or alters variables in the scope in which it's called, this is only a problem to the extent that one has been careless with one's variables and scoping anyway.
Set against this danger, is the advantage of (A) being able to give the subroutine access to all the variables existing in the calling scope without and (B) being able to pass back not just a return value but a variable (or several vars) which can be manipulated.
On the other hand, if you just don't see why I would be worried about this in the first place, follow the readmore tag...
In the set of scripts I'm working on now I often have to interrogate a database to get three records from three different tables, in the form of three hashrefs, which I then do a wide range of different operations on. The main place for code re-use here is the getting of the data from the db, not what I do with it. Now the problem is, my subroutine needs to return a hashref which I can then use freely after the subroutine is done. One way I tried to do it was:
First I have a module, let's call it
booker.pm, which looks like this:
package booker;
require Exporter;
our @ISA = ("Exporter");
sub
Get_Booking_Course_Event
{
my $BookingID = shift;
my $dbh = DBI->connect("DBI:mysql:database=foo", "bar", "baz") or
+die $DBI::errstr;
my $sth;
$sth = $dbh->prepare("SELECT * FROM booking WHERE BookingID = $Boo
+kingID") or die $dbh->errstr;
$sth->execute();
$ref_booking = $sth->fetchrow_hashref;
$sth = $dbh->prepare("SELECT * FROM event WHERE EventID = $ref_boo
+king->{'EventID'}") or die $dbh->errstr;
$sth->execute();
$ref_event = $sth->fetchrow_hashref;
$sth = $dbh->prepare("SELECT * FROM course WHERE CourseID = $ref_b
+ooking->{'CourseID'}") or die $dbh->errstr;
$sth->execute();
$ref_course = $sth->fetchrow_hashref;
}
our @EXPORT = qw/Get_Booking_Course_Event/;
1;
And
use booking; in each of the scripts.
My first attempt at calling this subroutine was
my $ref_booking;
my $ref_event;
my $ref_course;
&Get_Booking_Course_Event($ref->{'BookingID'});
# the arg in this sub cd be one of a number of
# things depending on where in the script we are
do_stuff_with($ref_booking, $ref_course, $ref_event);
Now, the main problem with that is it doesn't work. Because the three variables I declare before calling the subroutine don't get into the namespace of the subroutine... let alone out of it again with something useful in them.
My next try was to call it without declaring any variables and then use the variables from the module's namespace, with fully-qualified variable names:
&Get_Booking_Course_Event($ref->{'BookingID'});
# the arg in this sub cd be one of a number of
# things depending on where in the script we are
do_stuff_with($booker::ref_booking, $booker::ref_course, $booker::
+ref_event);
That seems to work. But I still don't totally like it. For one thing, I quite often call this sub more than once in the same script. And even though I have error checking, I'm slightly worried that
something might go wrong later on, and then the second time I do my operations with
$booker::ref_booking etc, I'm still working on the first ones.
What I'd really like is to be able to localise the variables that the subroutine returns. Or even better, have them returned ready-localised. Which is what finally led me to the desire to be able to make my subroutine be in the same name space as the calling scope. Then I'd declare
$ref_booking and the others in the subroutine. And I also wouldn't have to keep creating and destroying my database handle, because I could use one I created earlier, which is in scope at the point where I call the sub.
So that's what I'd like to be able to do. And I don't think I can. So... tell me I can; or tell me I'm crazy to want to.