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

This one's for all the perlguts hackers.

One of the things I've wanted to do from time to time is have a sub remember it's arguments in a given context. (Mostly for Iterators) While iterators are easily implemented with closures (and I often use them but that's not the point), I wish there was a more natural DWIM way.

One trick I've done is to use save the args in a hash keyed by stringifying the return from caller(1). This works ok but if in a while loop or other looping construct, it fails if the loop is exited prematurely or if the lexical arguments are declared in an scope outside of the loop. In other words, if the loop is later executed again, it will start off from where it left off which is not DWIM.

One thought I had was, what if we could insert a value into the caller's lexical scope? If you used Padwalker's peek_my from within the sub, you could see if your lexical was set. If not you could do your initialization and set the my var in the caller's pad using a yet uncreated function called "poke_my", then (theoretically) when the loop is exited, the value would be destroyed (theoretically) so if it's reentered, the subroutine will correctly see that it has not been called from this "context" and reinitialize itself correctly (theoretically). :)

Does this sound um... sound? From my limited understanding of internals, I would think you could grab the caller's xcv_padlist, add the suitably mangled var name (to avoid collisions) to the scratchpad and set it's value. I thought I remembered reading that the pad isn't always de-allocated as an optimization. If that is true I would think that would foil this.

Any suggestions or comments would be appreciated.

-Lee

"To be civilized is to deny one's nature."

Replies are listed 'Best First'.
Re: Iterating & Playing with Caller's Pads
by RMGir (Prior) on Sep 27, 2002 at 11:59 UTC
    Cute.

    But massive overkill, I'd think.

    Why not just get the user to pass you a reference to a lexical you can use to store your magic? Or even use as your key...

    The iterator syntax is cute, but is it worth that much effort? It would likely break at every point release, and I don't think you'd get much buyin from p5p for adding tests to make sure this kind of stuff keeps working from release to release...
    --
    Mike

      I'd just like to be able to write operators that act like any other builtins. The limitation isn't stopping me from doing anything I need to do. It just irks me that you can't do it. It just seems to me that I should be able to say somthing like
      while( my @els = elements @array,3){ .. do something } # instead of my $iter = make_iter(@array); while (my @els = @{ $iter->() } ){ .. do something }
      I would also think that it might solve part of the problem of implementing coroutines and continuations.

      Excuse my ignorance, what do you mean by "point release", do you mean newer versions of perl? I would think that behavior wouldn't change that much but I honestly have no idea.

      -Lee

      "To be civilized is to deny one's nature."
        It just irks me that you can't do it. It just seems to me that I should be able to say somthing like ...
        ... this?
        { my $pos = 0; sub elements(\@$) { my($ar, $n) = @_; my $range = $pos + $n < @$ar ? $pos + ($n - 1) : $#{$ar}; my @ret = @$ar[ $pos .. $range ]; $pos += $n; return @ret ? @ret : (); } } my @ar = qw( foo bar baz one two three ichi ni san xxx ); while(my(@chunk) = elements(@ar, 3)) { print "got: @chunk\n"; } __output__ got: foo bar baz got: one two three got: ichi ni san got: xxx yy
        Or am I missing the point?
        HTH

        _________
        broquaint

Re: Iterating & Playing with Caller's Pads
by diotalevi (Canon) on Sep 27, 2002 at 13:26 UTC

    A conversation regarding user-perl hidden lexicals popped up in the chatterbox recently. There was a thought that if you prepend the variable name with '&' then it is effectively inaccessible from user-side perl. The rest of the conversatin wasn't relevant but that's still an interesting idea. I still don't understand internals well enough to know what happens to PADLISTs on entering/leaving a lexical scope but hey...

    Or... on further consideration now that you're potentially stomping on other people's lexicals I would think that your variable key should also incorporate some disambiguation between individual instances of yourself - consider including your own address in the variable name so if the user starts two iterators then both don't write on each other. Obviously this raises other concerns about circular references - your iterators are now responsible for cleaning up every CV context you've been in on iterator destruction (looks like you should turn that into an object now).

      My thinking on the lexical naming of the var that is hidden in the scope would be a \w friendly mangling of the function and it's args. That way you could have multiple calls in the same scope.

      Just to clarify, because my example did not is that I'm interested in Lazy Iterating.

      -Lee

      "To be civilized is to deny one's nature."

        And if your function is called with the same arguments in a different instance? You can use line number to disambiguate between instances that aren't on the same line but I'm not sure how you keep track of different instances with the same arguments on the same line. Unless... keep track of the specific instances of your arguments - pointers perhaps. Those are only four bytes (on my platform anyway) and would be cheap to keep track of. If you do this I'd like to see the code.

Re: Iterating & Playing with Caller's Pads
by John M. Dlugosz (Monsignor) on Oct 18, 2002 at 14:49 UTC
    It's been said that Perl6 will have the ability to manipulate the caller's scope (e.g. for adding on-exit code), so this should be doable as you suggest.

    I agree that it would require something more to make it work as well as the builtins, perhaps an Attribute that tells the compiler to add an implicit parameter for you.