Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

how to declare a local *subname=sub{}?

by perl-diddler (Chaplain)
on Oct 30, 2016 at 22:07 UTC ( #1174968=perlquestion: print w/replies, xml ) Need Help??

perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

I had things working and then decided to break this program into 3 parts. The 1st part had a jump-table, of sorts, where a bunch of anon subs were called depending on what keyword matched. Some were named because they were reused by other routines.

When I turned the 1st part into its own package, the call to the subs via the "local" name, stopped working.

Created 2 call types in a test. asub can only be called if I don't declare it w/local -- Can't I declare it?

bsub is local to a procedure, for it, the local call has no effect (bsub is callable from within "do_internal_sub" either way. Oddly, even though the "local *bsub" would seem to include "stuff" in its lexical scope, instead of the eval telling me that bsub was not set, it says it can't find bsub.

So why does the "local *bsub" have no effect and why does the inclusion of "local *asub" prevent it from being callable in "stuff", below?

#!/usr/bin/perl { package Do::Stuff; use warnings; use strict; use P; sub setup{my $p=shift; my $c=ref $p||$p; bless $p={}, $c unless ref $p; $p; } #local *asub; *asub = sub { P "asub called with %d param%s%s.", 0+@_, -1+@_?"s":" +", @_ ? P(" (%s)", \@_) : ""; 1}; local *bsub; sub do_internal_sub() { my $p=shift; *bsub = sub{ P "Called with %d param%s%s.", 0+@_, @_-1?"s":"", @_ ? P(" (%s)", \@_) : ""}; P "calling bsub: %s", &bsub("one",2,"III"); } sub stuff() { my $p=shift; P "In stuff"; eval {&asub("stuff")}; if ($@) { P "asub call gave: $@"; } eval {&bsub("more", "stuff")}; if ($@) { P "bsub call gave: $@"; } } 1} package main; use warnings; use strict; use P; my $p=Do::Stuff->setup; P "calling stuff..."; $p->stuff; P "calling internal_sub: %s", $p->do_internal_sub;

P.s - Obviously, I can hack together something that will work, but there sure seem to a few inconsistencies which always tend to bug me... Thanks!

Replies are listed 'Best First'.
Re: how to declare a local *subname=sub{}?
by LanX (Archbishop) on Oct 30, 2016 at 22:35 UTC
    > So why does the "local *bsub" have no effect and why does the inclusion of "local *asub" prevent it from being callable in "stuff", below?

    local is a runtime command which restores at the end of the scope, given by the block markers here.

    And the declaration of subs is a compile-time effect.

    Obviously you are calling stuff after the restoration.

    HTH your code and intentions are not very clear, this smells like a XY problem

    Update:

    If you want to manipulate (so called monkey patching ) which routines are called within a sub, you have to manipulate the symbol table right before calling the surrounding sub.

    That is

    stuff(); #original { local *asub = $code_ref; stuff(); # new behavior local (sic) only } stuff(); #original

    update

    But you don't need local if you don't need it to be restored.

    I'd rather suspect you might want to look into closures and how to call lexical coderefs

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      Possibly because 1 question expanded into 2?

      1) Why does including the "local *asub" prevent it from being called in "stuff"? I.e. if you uncomment the "local *asub" line, it can't be called in "stuff" -- this is the main question.

      2) Why does including the "local *bsub" (of an interior function -- different animal) appear to be ignored in "sub stuff".

      In playing w/the test case, I noted that I could put the "local *'subname', outside of an interior sub where it is implemented -- but that doing so was "ignored", from-the-standpoint of trying to call it from "outside" its containing sub (i.e. the attempted call to &bsub in "stuff()". That curiosity created question#2...

      Note, I wouldn't expect calling an interior function (as stuff tries to call bsub) to necessarily work -- just that I found that trying to pre-declare it with local didn't even change the error message when "stuff" tried to call it. I thought that was strange.

      Main Q is why or how might I _declare_ asub, or is
      *asub = sub{...} equivalent to
      sub asub {}?

      It's that I'm not used to seeing an undeclared name on the LeftHandSide without it being flagged by "strict" as it not being declared.

        2) already answered, sub declarations are done at compile time, any local commands prior in text have no effect, they are run time, ie happen later.

        Additionally you are explicitly setting the content of *bsub whenever you call the internal function.

        update

        Please read perlsub#Temporary-Values-via-local() and following

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

        > Main Q is why or how might I declare asub, or is  *asub = sub{...} equivalent to  sub asub {}

        Almost equivalent, the prior has a runtime effect the latter only compile time.

        See perlmod for phases of execution and do some experiments checking the availability of subs.

        But I'm repeating myself now. ...

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

        1) because local restores the (here undef) value of the code slot of *asub at the end of the scope, which is reached before stuff() is called.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

        > Possibly because 1 question expanded into 2?

        Please see  How (Not) To Ask A Question

        Though my answers include many valuable informations, this thread will be of no help for the majority, because your intentions are unclear and your code mangled

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

Re: how to declare a local *subname=sub{}?
by haukex (Chancellor) on Oct 31, 2016 at 09:39 UTC

    Hi perl-diddler,

    Although LanX has already explained several things in various posts, one keyword that has not been named in this thread is dynamic scope, personally I find this easiest to remember. local only affects things in the current dynamic scope - as explained in Temporary Values via local(), it changes the value of a global at runtime, which will also affect any functions called at runtime in the scope where local is in effect, but at the end of the scope, the previous value of the variable is restored. An example:

    our $foo = "foo"; { local $foo = "bar"; sub quz { print "\$foo = $foo\n"; } } quz(); # prints "$foo = foo"

    Once you realize what local is doing, you'll see that the above code is basically equivalent to this:

    our $foo = "foo"; { $foo = "bar"; sub quz { print "\$foo = $foo\n"; } $foo = "foo"; } quz(); # prints "$foo = foo"

    Note how quz is called after after the original value of $foo is restored. So I hope that explains why your local *asub = sub ... would cease to have any effect once Perl has completed executing your block { package Do::Stuff; ... }.

    Your use of *bsub warrants some closer inspection though. In your code example, if you call $p->stuff after $p->do_internal_sub, suddenly the call the bsub works! It's a bit complicated, but if you keep in mind the distinction as to when which line is executed, it makes sense. I'll explain using this example:

    #!/usr/bin/env perl use warnings; use strict; sub foo { print "orig foo\n" } sub bar { print "orig bar\n" } { local *foo = sub { print "local foo\n"; }; local *bar; sub quz { foo(); eval { bar(); 1 } or warn "calling bar failed: $@"; } sub baz { *bar = sub { print "local bar\n"; }; } quz(); # first call to quz baz(); # first call to baz quz(); # second call to quz } quz(); # third call to quz baz(); # second call to baz quz(); # fourth call to quz __END__ Subroutine main::foo redefined at local_ex.pl line 8. local foo calling bar failed: Undefined subroutine &main::bar called at local_ex +.pl line 12. local foo local bar orig foo orig bar Subroutine main::bar redefined at local_ex.pl line 15. orig foo local bar
    1. When local *bar; is executed, its value is cleared (Update: this is the intended effect of local, although the documentation is admittedly a little hard to find). Thus, the first call of quz();, while the local *bar; is still in effect and baz has not been executed, will not have a sub bar to execute.
    2. Then, we call baz();, which assigns a value to the currently still localized bar, so the second call of quz(); prints the two "local ..." strings.
    3. Once the block is exited, the effect of local ends and the original definitions of foo and bar are restored, so the third call to quz(); prints the two "orig ..." strings.
    4. Then we call baz(); a second time, which redefines bar. Now what's important to remember here is that the effect of the local *bar; happened at runtime, so its effect is already over! Which means that now, baz globally redefines bar.
    5. The final call to quz(); now still sees the original foo, but bar has been redefined globally.

    Having said all that, I think local *foo = sub {} is one of those things that should only be used very rarely (one of those "use only if you know what you're doing and why" things). Aside from the plain wackyness of the above example code, one of the main arguments is that, as always, fiddling with global definitions can cause unintended consequences in other places in the code: if you redefine foo and then call another function, who's to say whether someone down the call tree is depending on the original definition of foo?

    The two much better and "more modern" solutions IMO are simply passing around references to (anonymous) subs, or an OO design with classes where methods can be overridden in subclasses; based on what you've written, I suspect the latter might be appropriate in your case, but you'd have to explain a bit more the bigger picture of what you're trying to do.

    Update: Yet another reason not to use local *bar; - it localizes everything named bar! Example in readmore tags:

    A few more notes on your code:

    • "sub stuff() {" - prototypes are ignored on method calls.
    • &asub("stuff") - the &foo() style of calling subs disables prototype checking and changes the behavior of @_ (perlsub) - in modern Perl, it has become another one of those "use only if you know what you're doing and why" things.
    • eval {...}; if ($@) ... is discouraged, use eval {...; 1} or ... instead, see e.g. Bug in eval in pre-5.14 or the "Background" section of Try::Tiny.
    • When posting code examples, the easier it is to run for everyone, the better. In this code, your P appears to be doing the same as printf, so if you'd just use printf, people could just download and run your code instead of installing a new module.

    Hope this helps,
    -- Hauke D

    Updated wordings in a few places.

Re: how to declare a local *subname=sub{}?
by Marshall (Abbot) on Oct 31, 2016 at 04:14 UTC
    I am very unsure as to your question and the intent of your code? I don't see the need for type glob (*something). What is your objective?

    #!/usr/bin/perl use strict; use warnings; # dispatch table... # my %table = ('x'=>\&ABC, 'y'=> sub {print "Anon sub for key 'y' in table\n";} ); sub ABC{print "sub ABC called\n";}; # call routines in the dispatch table... $table{'x'}->(); # prints: "sub ABC called" $table{'y'}->(); # prints: "Anon sub for key 'y' in table" # local can only "mask" an "our" or a global variable, like # perhaps as in the common idiom: # my $file = do { local $/; <FILE> }; # which "slurps" an entire file into the $file variable.<br> # A "my" variable cannot be "localized" our $xyzzy = 33; { local $xyzzy = 55; print "$xyzzy\n"; #prints 55 } print "$xyzzy\n"; #prints 33
      I am very unsure as to your question and the intent of your code?

      I share your uncertainty. I think perl-diddler may have invented yet another way to write spaghetti code without using goto. A dispatch table approach such as you exemplify tends to be my knee-jerk approach to the sort of problem that I imagiine gave rise to the OP.

      # local can only "mask" an "our" or a global variable ... ... # A "my" variable cannot be "localized"

      I tend to think of this a bit differently. My conception is that a my variable can be completely "localized" or isolated within a scope, whereas an our (or package global) variable cannot. The potential scoped isolation of lexical variables makes it very easy to reason about them, a great advantage.

      c:\@Work\Perl>perl -wMstrict -le "our $xyzzy = 33; print qq{A: $xyzzy}; { local $xyzzy = 55; print qq{B: $xyzzy}; zot(); print qq{C: $xyzzy}; } print qq{D: $xyzzy}; ;; sub zot { $xyzzy = 99; } " A: 33 B: 55 C: 99 D: 33 c:\@Work\Perl>perl -wMstrict -le "my $xyzzy = 33; print qq{A: $xyzzy}; { my $xyzzy = 55; print qq{B: $xyzzy}; zot(); print qq{C: $xyzzy}; zonk(); print qq{D: $xyzzy}; } print qq{E: $xyzzy}; ;; sub zot { $xyzzy = 99; } ;; sub zonk { my $xyzzy = 9999; } " A: 33 B: 55 C: 55 D: 55 E: 99

      Update: But then there's PadWalker...   (sigh)


      Give a man a fish:  <%-{-{-{-<

        We aren't that far off.
        #!usr/bin/perl use strict; use warnings; our $xyzzy = 33; print "A: $xyzzy\n"; { local $xyzzy = 55; print "B: $xyzzy\n"; zot(); # changes $xyzzy altough no return value # zot() can modify a global variable print "C: $xyzzy\n"; } # $xyzzy is now back at the A: value print "D: $xyzzy\n"; sub zot { $xyzzy = 99; #set global "our" var } __END__ A: 33 B: 55 C: 99 D: 33
Re: how to declare a local *subname=sub{}?
by Corion (Pope) on Oct 31, 2016 at 09:48 UTC

    As you haven't told us what problems you're trying to address it's hard to give you a good direction to investigate in.

    There is an experimental feature named lexical_subs that is supposed to declare lexically scoped subroutine names in the following form:

    use feature 'lexical_subs'; sub foo { my sub secret_foo { my($arg) = @_; return "Hello $arg"; }; my( $bar ) = @_; my $res = secret_foo( $bar ); return "Result is '$res'"; } print foo('World');

    But as far as I know, the feature never left its experimental status and has the same interactions with lexical variables as the traditional approach using lexical variables has. If you want to use local subroutines without polluting the namespace, just use anonymous subroutines:

    use feature 'lexical_subs'; sub foo { my $secret_foo = sub { my($arg) = @_; return "Hello $arg"; }; my( $bar ) = @_; my $res = $secret_foo->( $bar ); return "Result is '$res'"; } print foo('World');

    Using dynamic scope is usually fraught with problems as debugging dynamic scope is difficult. There is a reason why lexical scope was a great thing for Perl 5, and while it is sad that there is no convenient lexical scope for subroutines, working around this with dynamic scope is a bad idea.

      "There is an experimental feature named lexical_subs ... But as far as I know, the feature never left its experimental status ..."

      This will probably be non-experimental in the next stable release (i.e. v5.26). See "perl5252delta: Lexical subroutines are no longer experimental".

      Update: Added "[non-experimental lexical_subs]" to title.

      — Ken

Re: how to declare a local *subname=sub{}?
by LanX (Archbishop) on Oct 31, 2016 at 00:08 UTC
    Please inform yourself about the differences of local vs my in Perl (also vs our )

    Your code and questions reveal a fundamental misunderstanding of local.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

Re: how to declare a local *subname=sub{}?
by Anonymous Monk on Oct 30, 2016 at 22:26 UTC

    The title and problem description seem incompatible -- its unclear what is being asked

    Consider this

    $ perl -wle " { local *foo = sub { warn 6}; foo; } foo; " Unquoted string "foo" may clash with future reserved word at -e line 1 +. Unquoted string "foo" may clash with future reserved word at -e line 1 +. Useless use of a constant ("foo") in void context at -e line 1. Useless use of a constant ("foo") in void context at -e line 1. $ perl -wle " { local *foo = sub { warn 6}; foo(); } foo(); " 6 at -e line 1. Undefined subroutine &main::foo called at -e line 1.

    What is your question?

Re: how to declare a local *subname=sub{}?
by Anonymous Monk on Oct 30, 2016 at 22:16 UTC
    Is there a version with no P;?

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1174968]
Approved by GrandFather
Front-paged by stevieb
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (8)
As of 2020-01-21 12:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Notices?