in reply to access to my variables from other subs
Thanks Adam,
Yes, local would probably be the trick, but I
don't have that luxury. This thing is supposed
to plug into a lot of existing code that uses "my"
variables. It will work if I eval the manufactured
sub each time, I was just trying to find a way around
that because it is such a performance hit. I'm
becoming convinced that what I want to do is impossible,
and for all I know, it's impossible for a good reason :-)
From what I've been reading, the "my" variables live
on what is known as a "scratchpad", what I was looking
for was a way for the manufactured sub to get access
to that scratchpad. Of course the scratchpad changes
each time the parent sub is called, so the pointer to
it in the manufactured sub would have to change with
each invocation. I wonder if the rfc period for perl 6
is closed :-)
Hopefully merlyn will chime in here and tell me why
it's such a bad idea to try and do what I am trying to
do, because there is probably a good reason not to
allow it, I just don't know what that is yet. :-)
RE (tilly) 2: access to my variables from other subs
by tilly (Archbishop) on Oct 21, 2000 at 06:01 UTC
|
I am not merlyn, but will I do? :-)
What you are trying to do has serious conceptual problems
for reasons I tried to explain in RE (3): BrainPain-Help.
However instead of saying what it is you want to do, why
don't you explain what it is that you are trying to do?
Then instead of trying to fight the design of the language
we might be able to tell you how to get it done working
with the language.
For instance you could always pass the variables you want
access to in as arguments to the function. Or if it is
more convenient, pass in references to the variables
instead. (Not strictly needed, but it tends to warn the
caller that you may intend to do stuff to the variable.)
I am trying to figure out what you need direct access to
your caller's lexical variables for or why providing that
wouldn't ruin the entire point of having them - and I am
failing... | [reply] |
|
Hey Tilly, you're right up there in god status in my mind, so, sure you'll do :-)
So, what am I trying to do? I am trying to provide an easy to use output functionality to perl programmers, ala Template::Toolkit, embperl, etc... I do understand how to do it by passing variables or eval-ing the sub each time. Both have drawbacks in my mind. The drawback to the passing the variables is that is no longer quite as easy as envisioned: If you decide to display a new variable, you can't just change your template and be done, you now must change the code that passes in the hash or hashref to the display function. Eval-ing each time works ok, but it is molasses slow.
As for ruining the point of having lexicals, I hope I'm not quite proposing that... The lexicals would only be accessible to the sub that made them, and to subs that were created by the sub that made them. I'd say they'd still be pretty safe from falling into the soup.
Now I don't know anything about perl internals except from what I read in Advanced Perl Programming but it tells me that lexicals live on a scratchpad for each sub. I think it would be useful if subs created in a sub had access to it's parent's scratchpad, and if the parent that created it is no more, then the last instance of the parent sub that was created. I'd even accede that the created sub must be called from within the parent sub -- but with access to the scratchpad of the current instance of the parent sub, not the scratchpad of the instance of the parent sub that did the actual creation (which may be long gone...).
I know this is confusing, and I'm probably not explaining myself well, but if you get what I'm saying... what do you think of it?
thanks
| [reply] |
|
God status? All too human I am afraid!
You remind me of one of the key differences between
Christian and Buddhist monks. Both have key figures in
their religion that they revere. But where Christians set
out to worship their founder, Buddhists seek to match what
theirs did. Of course a Buddhist monk believes that they
have it easier than Siddartha Gautama because the monk has
the descriptions of those who went before to work from while
Siddartha was working blindly. (Likewise a Christian monk
believes that they have it easier than Christ did, but for
rather different reasons.)
The point is that (whatever your religious beliefs) it is
better to emulate the Buddhist monk. Don't deify heros,
seek to build on what they discovered. :-)
Anyways to your problem. You are asking to be able to break
the encapsulation provided by my in some controlled way.
You can't. (At least not without mucking in the internals.)
And there are good reasons for that.
The key principle here is that allowing one programmer to
make assumptions about another programmer's code leads to
unmaintainable messes. For instance take your template
idea. Suppose that everyone was using this template. I
have some code that uses it, code in which I am using $x
and $y for other reasons. (Those are generally bad names,
but I have used them before when the parallel to Cartesian
coordinates helped me keep straight the grid problem I was
thinking about.)
Unknown to me someone decides to add an equation solver to
your template. This will look for variables like $x, and
$y, then try to solve equations. This programmer "knows"
that those are bad variable names, and adds them assuming
that nobody has used them. I know that code worked, and I
have no reason to expect that it would suddenly change.
A couple of months later in testing it turns out that my
grid code is horribly broken. Now I have to go back to my
code and do diagnostics on the whole project before I track
down how it worked, that it worked, and finally spiral
outwards until I find the the template change which broke
it. (Assuming that I ever think of looking there.) Once
found the person who broke it now has a ton of code that
depends on that template. So I have to go back and rename
all of my variables, leading to further integration and
testing delays.
This is not good from a scheduling point of view, nor from
the episodes of ballistic programmers which the imagination
can easily fill in.
A far better approach is to have my lexicals be
truly private so that my code cannot be broken by the
addition of something to the template, and have the template
export an object (data structure, whatever) that nicely
encapsulates its functionality in such a way that you can
add neat stuff, I don't have to care about new potential
functionality being added because it cannot come into
conflict with me.
This is why there is a nice warning in perldoc about
local not being what you want and my being better.
This is also the software engineering principle known as
"Loose Coupling" that you see at the top of the page from
time to time...
| [reply] |
|
In a similar situation, I'd use an object. If you need to carry context and variables around, blessing a hash is a pretty convenient way to have a bundle of data that knows which operations it supports.
The beauty there is, depending on your encapsulation paranoia, you still have access to the hash through the reference, so working with its data is as easy as usual.
Nesting subroutines is a conceptual no-no in Perl... hard to maintain, hard to debug. The closures route is a little tricky if you're not familiar with functional programming (but can be very useful in other cases).
If you can live with enforced lexical context through a reference, you'll be a lot happier with objects.
| [reply] |
|
Eval-ing each time works ok, but it is molasses slow.
You don't have to use eval in order to create a new closure. As I recently pointed out, creating a closure doesn't even recompile the subroutine. So I wonder if your concern about speed is based on benchmarks or just an expectation on your part.
The drawback to the passing the variables is that is no longer quite as easy as envisioned: If you decide to display a new variable, you can't just change your template and be done, you now must change the code that passes in the hash or hashref to the display function.
A real code example would sure go a long way here. There are lots of ways to build something that knows about a lexical variable. I have a hard time imagining why passing in \%hash explicitly is such a hardship, but that is probably because I haven't seen any code that really shows what you are trying to do -- just the abstract problem that you think should be the solution.
$closure= maker( \%hashToUse );
$closure->();
$obj= Package->new( \%hashToUse );
$obj->Display();
use Package qw( setVar printVar );
setVar( %hashToUse );
printVar();
use Package qw( %Template );
$Template{var}= \%hashToUse;
print $Template{output};
my $Print;
use Package ( \$Print, \%hashToUse );
$Print->();
tie $output, "Package", \%hashToUse;
print $output;
tie \*STDOUT, "Package", \%hashToUse;
print;
Okay, are any of those silly enough for you? (:
-
tye
(but my friends call me "Tye") | [reply] [d/l] [select] |
|
I think it would be useful if subs created
in a sub had access to it's parent's
scratchpad, and if the parent that created it is no more,
then the last instance of the parent sub that was created.
I'd even accede that the created sub must be called from
within the parent sub -- but with access to the scratchpad
of the current instance of the parent sub, not the
scratchpad of the instance of the parent sub that did the
actual creation (which may be long gone...).
A lot of people think it should work that way but I
don't think it ever will. In a way it does and it a way
it doesn't act like you'd like.
One, as I understand it, there is just one big scratchpad
for the my'ed variables. I do know that all the
subs are created at the same "level". They are all first
class subroutines of the package they are in. Look at this
contrived example:
#!/usr/bin/perl -w
use strict;
sub one {
my $inside=1;
sub two {
$inside++;
my $inner=$inside+1;
print "$inside $inner\n";
}
print "$inside "; # no return there
two();
}
two();
two();
two();
one();
two();
&main::one;
&main::two;
#### prints
# 1 2
# 2 3
# 3 4
# 1 2 3
# 3 4
# 1 4 5
# 5 6
In that example, from the last two lines, you can
quickly see that perl creates both subs as tho they
each were independent. What happens is when perl sees
the sub called inside the enclosure, it creates
the $inside variable right away and gives it
to two. Now, since one hasn't been
run yet $inside doesn't exist so perl screams:
Variable "$inside" will not stay shared at ./test line 7.
To let you know that when you run one that first
$inside will be brutally trampled on...
The fact is, since the subs are supposed to be the
same level but we stuck one inside the others closure,
perl does its best to make it all work out. It even goes
back and fixes $inside after the first time we
use one. (well not really, actually it pre-made
a scratchpad entry for $inside and used that
reference in two! Then every time we call one
after that, it makes a new scratchpad entry and has no
idea it is supposed to tell two about it, since
they aren't linked together! It's just that perl told
two the hidden real name (reference) of the first
$inside it was making for one.)
In effect, what you are asking for is a degenerate
subroutine that is a child of one but perl has
no way of making one and honestly doesn't need to. What
it does it much cooler. If you want a sub that is
only used inside another sub and can see the current
values, try this:
#!/usr/bin/perl -w
use strict;
sub three {
my $inside=1;
my $four = sub {
$inside++;
my $inner=$inside+1;
print "$inside $inner\n";
}; # notice the semicolon, this is a statement!
print "$inside ";
&$four();
&$four();
}
three();
three();
##### returns
# 1 2 3
# 3 4
# 1 2 3
# 3 4
Does that help at all? Just remember that there is only
one scratchpad and code has no idea where it is. Only what
the compiler told it. =)
--
$you = new YOU;
honk() if $you->love(perl) | [reply] [d/l] [select] |
|
|