Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Re: access to my variables from other subs

by joe (Acolyte)
on Oct 21, 2000 at 05:41 UTC ( [id://37772]=note: print w/replies, xml ) Need Help??


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. :-)

  • Comment on Re: access to my variables from other subs

Replies are listed 'Best First'.
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...

      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

        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...

        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.

        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")
        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)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://37772]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (5)
As of 2024-04-19 23:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found