Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Re: Is modifying the symbol table to redefine subroutines evil?

by perrin (Chancellor)
on Apr 11, 2007 at 22:12 UTC ( [id://609506]=note: print w/replies, xml ) Need Help??


in reply to Is modifying the symbol table to redefine subroutines evil?

Yes. Next question.

Seriously, what's wrong with a much easier approach like this?

our $FOO_HAS_BEEN_CALLED; sub foo { if (not $FOO_HAS_BEEN_CALLED) { call_me_only_once(); $FOO_HAS_BEEN_CALLED = 1; } }
Or maybe this code belongs in a BEGIN block.

Replies are listed 'Best First'.
Re^2: Is modifying the symbol table to redefine subroutines evil?
by shmem (Chancellor) on Apr 11, 2007 at 22:32 UTC
    Yes. Next question.
    Next question: why?
    Seriously, what's wrong with a much easier approach like this?

    Somebody could mess with the package global $FOO_HAS_BEEN_CALLED and blow things up, and call_me_only_once() gets called twice. Of course, call_me_once() disposing of itself properly after being called would make damn sure it isn't a second time :-)

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      Why? Because it solves a simple problem in a complex and convoluted way.

      Somebody could mess with the package global $FOO_HAS_BEEN_CALLED and blow things up

      The "somebody could mess with it" argument has never been valid for Perl. Of course somebody could mess with it! Somebody could mess with absolutely everything in your program. If you want to prevent possible accidental changes, you could make it a lexical instead and let foo() be a closure around it. But no one mentioned security as the reason for this.

      I still think it sounds like setup code that should be in some kind of BEGIN or INIT block.

        Of course somebody could mess with it! Somebody could mess with absolutely everything in your program.
        Agreed. "Somebody" could even be messing with the symbol table entry of *foo, in which case the assumed protection of it, is completely bogus.
        Because it solves a simple problem in a complex and convoluted way.

        I fail to see how re-arranging a single type glob entry is more convoluted (or complex) than having a state variable sitting around which is changed only once and tested every time the sub is called. The latter seems more like useless clutter to me.

        One problem with assigning an anonsub to a typeglob is that it will be reported as anonsub by confess and friends.

        But it's clear, concise code, its intent is clear. It might be extravagant or even bizarre, but not evil in the sense of utterly bad practice.

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      Actually, there's no reason for it to be a package variable. I would have used a lexical. And bah! If someone shoots themselves in the foot by messing with internal state variables, it's their own foot. It's not like someone could accidently change the value of the variable.
Re^2: Is modifying the symbol table to redefine subroutines evil?
by chromatic (Archbishop) on Apr 11, 2007 at 23:19 UTC
    Seriously, what's wrong with a much easier approach like this?

    It could be much easier to read:

    if (not $FOO_HAS_BEEN_CALLED) {

    ... if it were instead:

    unless ($FOO_HAS_BEEN_CALLED) {

    Otherwise, it seems reasonable.

      Now that you mention it, I usually handle this kind of bail-out conditional with a simple test at the top:
      return if $foo_has_been_called;
      Then you don't have to indent the whole rest of the sub.
Re^2: Is modifying the symbol table to redefine subroutines evil?
by dewey (Pilgrim) on Apr 11, 2007 at 22:39 UTC
~dewey
      Here it is without globals:
      { my $foo_has_been_called; sub foo { if (not $foo_has_been_called) { call_me_only_once(); $foo_has_been_called = 1; } } }
Re^2: Is modifying the symbol table to redefine subroutines evil?
by tlm (Prior) on Apr 12, 2007 at 03:31 UTC

    There's nothing wrong with this approach. (I certainly hope there isn't because my own code is filled with instances of this pattern.) But it has always annoyed me to saddle a function with a test that is useful only once in its lifetime. It's a perverse sense of aesthetics, I admit it, but that is, at any rate, the motivation behind the gyrations... I was just wondering whether there were fundamental problems with the approach (other than its neurotic convolutedness).

    the lowliest monk

      There's nothing wrong with this approach. In fact, my own code is filled with instances of this pattern. But it has always annoyed me to saddle a function with a test that is useful only once in its lifetime. It's a perverse sense of aesthetics, I admit it, but that is, at any rate, the motivation behind the gyrations...

      Yeah, I'm concerned too with "this kinda things": see for example Doing "it" only once which was brought up here by Limbic~Region, but inspired by a post I made in p6l. There, the situation was somewhat lower level in that it boiled down not to a matter of mangling the symtable, but even the optree itself. Unfortunately most people didn't get it in both places, that is p6l and here: just pointing out that there are many WTDI and asking "what's wrong with this one?" Both me, LR and those who actually understood are perfectly aware that there are many ways around it, but still find them all somewhat unsatisfactory for some reason.

      To sum up what was going on in that thread, the mythological beast we were/are after is a statement modifier (or something like that) specifying that the statement itself must be executed once only, and then evaporate like it never existed, not being skipped upon a condition. There are fundamentally two categories of replies pointing out ways to do it without the mythological beast:

      1. use a counter and skip over the statement when it's positive;
      2. have a version of the loop with the statement that exits the loop when the statement is executed and then continue with a similar loop that does not contain the statement.

      In the former approach you check a condition also after you're sure it won't be true anymore a priori, and you don't want it to be. In the latter you avoid this with some sort of code duplication that in certain circumstances may also require you some code factorization that would be otherwise unnecessary. Currently I would personally use the former when I'm not concerned about performance and the latter when I am, with a preference for the latter even in the first cases because I find it conceptually and aesthetically disturbing to perform unnecessary operations even if any overhead they introduce may be negligible. I long for a solution that gives me the best of both solutions on the base of conceptual and aesthetical considerations in that it should be syntactically simple like the first one and avoid unnecessary operations, like the second.

      Somebody else, in that thread, pointed out that manipulations like those we were talking about were very common in early languages, but are now regarded as Evil™, and for good reasons that I can understand, the point still being that just like raw goto is also Evil™, Perl provides tamed forms of goto under the names of last, next and redo which are not and nevertheless make for very useful common Perl idioms. So, much in the same vein I do not long for generic means to achieve self-modifying code to be made excessively accessible, but for a tamed, specific, form of self-modifying code available as a teaspoonful of syntactic sugar simple enough to use from the typing-wise POV to make it elegant and appealing, and distinctive enough not to make it possible for one to use it inadvertently with the risk of being bitten in the neck by it.

      I was just wondering whether there were fundamental problems with the approach (other than its neurotic convolutedness).

      FWIW I find "its neurotic convolutedness" not to be terribly more relevant than the simple condition checking version.

      I can't think of a more fundamental problem with any code than being more complex than it needs to be, except maybe giving the wrong answer.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (2)
As of 2024-04-20 03:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found