Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

GOTO considered (a necessary) evil?

by gav^ (Curate)
on Jul 15, 2002 at 14:30 UTC ( [id://181789]=perlmeditation: print w/replies, xml ) Need Help??

We were reviewing the following code for part of a membership site I've been working on when another developer pointed out that I used a goto and this was evil. The code is:

RE_TIE: eval { tie %session, 'Apache::Session::MySQL', $session, { Handle => HH::DB->db_Main, LockHandle => HH::DB->db_Main } }; if ($@) { if ($@ =~ /^Object does not exist/) { $session = undef; goto RE_TIE; } else { print $cgi->header; die_nice('Database Error', 'Unable to connect at this time'); } }

The goto is to handle a "shouldn't really happen" case (somebody sending us a session id which is invalid). I like the use of goto because it doesn't distract from the "normal" case which a loop would. He suggested:

my $tied = 0; while (!$tied) { eval { tie %session, 'Apache::Session::MySQL', $session, { Handle => HH::DB->db_Main, LockHandle => HH::DB->db_Main }; if ($@) { if ($@ =~ /^Object does not exist/) { $session = undef; } else { print $cgi->header; die_nice('Database Error', 'Unable to connect at this time +'); } } else { $tied = 1; } }

Personally I think this is harder to read and it makes you read through to work out how the loop is exited.

I was wondering what the general consensus here was, is goto (in this case) really evil?

gav^

Replies are listed 'Best First'.
Re: GOTO considered (a necessary) evil?
by Abigail-II (Bishop) on Jul 15, 2002 at 14:45 UTC
    Well, if I had to remove the goto, I would rewrite it as:
    { eval { tie %session, 'Apache::Session::MySQL', $session, { Handle => HH::DB->db_Main, LockHandle => HH::DB->db_Main } }; if ($@) { if ($@ =~ /^Object does not exist/) { $session = undef; redo; } else { print $cgi->header; die_nice('Database Error', 'Unable to connect at this time +'); } } }
    But a redo (just like next and last is just a glorified goto. People who just balk at any sight of a goto have at best heard about Dijkstra's article Go To Considered Harmful (which wasn't titles bij Dijkstra, but by Hoare), but never read the paper, or not understood it. Dijkstra just warns that goto can easily lead to unstructured programs, he doesn't say it's evil all the time.

    Knuth has also written about this subject, in his Structured Programming with the Go To statement. His view is less far away from Dijkstra as that the titles suggest.

    Abigail

      But a redo (just like next and last is just a glorified goto.

      I consider this statement blatently false and missleading.

      • goto FOO causes is designed to cause execution to jump to the lable FOO anywhere in your script (provided no initialization would be neccessary to get there)
      • redo FOO will only work if the is designed to cause execution to jump to label FOO if it exists at some surrounding loop block. (allthough it will work w/warning if the label applies to some dynamicly surrounding loop)

      This typo generates an error...

      perl -le 'BAR: print 1; BAZ: for (2..3) {print; redo BAR; }'

      This typo causes an infinite loop...

      perl -le 'BAR: print 1; BAZ: for (2..3) {print; goto BAR; }'

      (Updated to be less pedantic... the point is, goto is for arbitrary jumping, redo is for controlled jumping to the begining of a loop. Could you live w/o redo if you had goto? yes. Does that mean you should just use goto and not bother with redo? no.)

        But a redo (just like next and last is just a glorified goto.
        I consider this statement blatently false and missleading.
        • redo FOO will only work if the label FOO exists at some surrounding loop block

        I consider this statement to be inaccurate and not quite pendantic enough for flaming someone! Observe:

        sub bar { no warnings 'exiting'; redo FOO; } FOO: { print "Hey!"; bar(); }
        The redo worked and the label did not exist in a surrounding loop block.
        This typo generates an error...
        perl -le 'BAR: print 1; BAZ: for (2..3) {print; redo BAR; }'
        This typo causes an infinite loop...
        perl -le 'BAR: print 1; BAZ: for (2..3) {print; goto BAR; }'
        That's a pretty weak example, because if you omit the typos, both
        perl -le 'BAR: print 1; BAZ: for (2..3) {print; redo BAZ; }'
        and
        perl -le 'BAR: print 1; BAZ: for (2..3) {print; goto BAZ; }'
        behave identically - looping infinitely.

        Abigail

Re: GOTO considered (a necessary) evil?
by VSarkiss (Monsignor) on Jul 15, 2002 at 15:29 UTC

    As Abigail-II correctly points out above, the point of Dijkstra's paper was that programs should be easy to read and follow. At the time the paper was published, many programs were in Fortran and COBOL which did not provide "block-structure" facilities that we're used to today. Rather, code was re-used by setting flag variables and calling various flavors of GOTO.

    Let me give a real-life example that I vaguely remember from <mumble> years ago. In Fortran-IV, if you had a piece of code that was used several times (say, calculating a pair of numbers), often you would not structure it as a subroutine, but would arrange for goto's around it. There are probably several reasons for this: in some cases it was (microscopically) faster, and a culture of "the way you do it". So off somewhere in your code you would have:

    C common calculation code C Fortran comments have a C in column 1 1100 if (argin(0) .eq. 0) j=1 .... C the equivalent of a "return" goto N
    (By the way, Fortran-IV labels were numeric.) You would then call this code in several places with:
    C do the common calculation assign 5500 to N goto 1100 5500 continue
    For those who haven't had the joy of reading stuff like this, an "assigned goto" allowed you to put the label that you want to execute next (in this case, 5500, the statement following the "goto 1100") into a variable, and jump there. That's how you effected a call and return without actually using CALL.

    Now imagine "passing an argument" when you're doing this. There's no automatic save and restore, so you have to do that yourself. And if you wanted the calculation to do something slightly differently in one case, you would introduce a global variable, which you would set and use in two different places. Nest this a couple of levels deep, and you have very hard to debug code. Stuff like this was where the term "spaghetti code" originated, I think.

    Dijkstra's point was to advocate not writing code like the above, but to use the less-popular call/return mechanisms. The title of the paper was meant to grab people's attention. (I didn't realize the title was Hoare's until I read Abigail's note.) The effect would probably be similar to some well-known computer scientist publishing "While loops considered harmful" today; it would make you sit up and take notice. Unfortunately, many people grabbed on to the title, but missed the main point.

    All this, of course, is a roundabout way of saying: write clear and easy-to-understand code, goto or no goto. There's nothing wrong with using a goto (or in Perl, its kin: next, last, redo) as long as it don't obscure how the code works.

(tye)Re: GOTO considered (a necessary) evil?
by tye (Sage) on Jul 15, 2002 at 15:43 UTC

    My favorite way to refactor loops that have the conditional in the middle is to use a subroutine because return in the middle of a subroutine seems an easy thing to understand (and to notice) when you come back to the code. It also avoids arguments with people who can't handle goto.

    I also prefer to force eval to return a true value when it succeeds rather than checking $@ since there are cases where $@ can give the wrong impression about whether the eval was successful.

    sub tieSession { while( 1 ) { if( eval { tie %session, 'Apache::Session::MySQL', $session, { Handle => HH::DB->db_Main, LockHandle => HH::DB->db_Main }; 1; } ) { return; } if( $@ !~ /^Object does not exist/ ) { print $cgi->header; die_nice( 'Database Error', 'Unable to connect at this time' ); } undef $session; } }
    So I don't consider goto inherently evil. I prefer your original code over your coworkers in at least some respects. I really hate the use of Perl bare blocks as loops and think it often leads to more spagetti code than even goto does (expecting people to notice a "redo" burried so deep inside in order to realize that a loop was suddenly created out of a bare block).

    But I wouldn't over golf for "excellence". Is the code working? There is probably something more important to be working on. (:

            - tye (but my friends call me "Tye")
      It also avoids arguments with people who can't handle goto.

      I'd agree with you, except I ran into at least one developer who thought that returns from the middle of subroutines were just as unreasonable as goto's. :)

      I'm in the "if I can understand what it's doing, it's fine" camp, myself. The goto in the original message was pretty clear, as is your example.
      --
      Mike

      there are cases where $@ can give the wrong impression about whether the eval was successful.

      Can you give an example of that?

        Sure:

        sub DESTROY { eval "1" } eval 'my $x = bless {}; die "ouch\n"'; print "($@)\n";
        which prints "()" not "(ouch\n)".

                - tye (but my friends call me "Tye")
Re: GOTO considered (a necessary) evil?
by atcroft (Abbot) on Jul 15, 2002 at 14:41 UTC

    I know of at least one general discussion on using GOTO (Would you use 'goto' here?), although I would guess there are likely more buried around here somewhere.

    I can see your use for it, but would be concerned that the way you coded it could perhaps cause an endless loop (although I will assume there is additional code that was excluded from the example to prevent this). To me, though, both are about equally as readable.

    Just my opinion, though, and I look forward to seeing the responses of others regarding the issue.

    Update: Actually, looking at both solutions, both tend to suggest that a continuous loop might be possible (or am I reading those wrong?).

Re: GOTO considered (a necessary) evil?
by FoxtrotUniform (Prior) on Jul 15, 2002 at 15:08 UTC

    In this case, I'm not particularly inclined to complain about the goto, though I find an explicit loop a bit easier to read (in that the branching structure's fairly well-defined; when I see a label, I look through all the applicable code for gotos, even if I'm fairly sure that there won't be more than the obvious one).

    In my experience, the "goto EVIL!" meme is propagated either by well-meaning but naive instructors who don't want to "confuse" beginning programmers with the vagaries of when gotos are appropriate, but don't want their students picking up bad habits, or by cargo-cult instructors who were taught that goto is uniformly bad, didn't bother to question the statement, and go on to teach others. Either way, it's irritating, but probably a net win.

    --
    The hell with paco, vote for Erudil!
    :wq

Re: GOTO considered (a necessary) evil?
by ferrency (Deacon) on Jul 15, 2002 at 15:55 UTC
    There are some benefits to using next or redo or a while or for loop instead of goto. One of those is, the perl compiler has more information about the structure of your code and its control flow, when you use something other than goto. I'm not going to make any uninformed assertions about the effects this has on optimization. Instead I'll bring up some more practical concerns.

    If your target label in a goto is undefined, perl won't complain until it tries to actually execute the goto, even when using strict and warnings. If your goto is handling a "once in a thousand years" case, then you're likely not to notice this typo until it's too late. The same thing happens when a label gets deleted or changed inadvertantly. A case where the same label is used twice can cause other confusion, and I haven't seen use strict or warnings catch that case either.

    If you are going to go through the trouble of making your code use strict, it's probably a good idea not to use code which subverts the strictness unnecessarily.

    This is not to say "don't use goto," even though I tend not to like it. Just be aware of the potential consequences.

    Finally, from perldoc -f goto:

    The goto-LABEL form finds the statement labeled with LABEL and resumes execution there ... The author of Perl has never felt the need to use this form of goto (in Perl, that is--C is another matter).

    Update:Abigail is right: labels on non-goto's have the same problem. I use labels on next when necessary, and much more frequently than I use goto. But I try not to use labels when they are not necessary, for the reasons I outlined above.

    Alan

      last, next and redo can take a label as well. And then you will have the same problems if you now point out with goto.

      Yet, next and friends having an optional label is seen as a feature, not a misfeature. It might very well be that the author of Perl had used the goto LABEL if he had not added optional labels to next, last and redo.

      Abigail

      If your target label in a goto is undefined, perl won't complain until it tries to actually execute the goto, even when using strict and warnings. If your goto is handling a "once in a thousand years" case, then you're likely not to notice this typo until it's too late.

      Ofcourse, you have the same problem if you use a subroutine/method call (though you would probably not use those in the same situation).

      Myself, I would prefer

      while (1) { .... .... last unless (EXPR); }
      Which makes very clear that this is a loop (even though it might not loop more than once 99% of the time) and it "forces" some structure (which is not always a bad thing), and it might make you see the problem from a new perspective - TIMTOWDI after all.
      -- Joost downtime n. The period during which a system is error-free and immune from user input.
Re: GOTO considered (a necessary) evil?
by John M. Dlugosz (Monsignor) on Jul 15, 2002 at 16:22 UTC
    In well-defined cases, such as jumping to the beginning or end of a loop, we have special keywords that mean this. It is not as bad as goto because you know what they mean in terms of structure, and we also have idioms that go with them.

    "Go back to the beginning" is a well-defined point, not a random jump into the middle of the logic.

    Check out the redo keyword. I've used it in cases like this.

Re: GOTO considered (a necessary) evil?
by particle (Vicar) on Jul 15, 2002 at 15:26 UTC
    after reading this thread and re-reading Would you use 'goto' here?, i'm beginning to think there should be optional warnings for goto usage. perhaps use warnings 'goto'; could warn on goto LABEL and goto EXPR, but pass goto &NAME.

    of course, this shouldn't be implemented without an agreement from the perl community that goto in the traditional sense should someday be deprecated.

    thoughts?

    ~Particle *accelerates*

      i'm beginning to think there should be optional warnings for goto usage. perhaps use warnings 'goto'; could warn on goto LABEL and goto EXPR, but pass goto &NAME.
      I think that is a very bad idea. Warnings are there to prevent programmers from making accidental mistakes. Warnings are done if variables have unexpected values (comparing integers with ==, adding undefined values, dereferencing non-references), when you try to do something that cannot be done (open a bi-directional pipe), use a deprecated feature (implicite @_), or did something you probably didn't want to do (exiting eval with next, mying the same variable twice in the same context</code>), etc, etc.

      But the use of goto is not deprecated, and you will not get much support to get it deprecated. And it's hardly likely someone types goto by accident.

      Warning should be used to prevent programmers from making mistakes - as soon as warnings will be misused to force a coding style upon programmers, use of use warnings and -w will plummit - and rightly so. Forcing a coding style, one way or the other, upon something else is Pythonesque.

      Abigail

      I disagree, for the many reasons others have already pointed out. The sentiment that you have to wipe out all gotos at all cost is cargo cult.

      Makeshifts last the longest.

      (Ten years from now, at the meeting of the Perl Programming Style Cabal...)

      "I want to remove regexes from the language. Lots of people uglify their programs by using it."

      "All in favor of removing regexes, say aye. All opposed, say nay."

      (A chorous of "Aye"s fills the room.)

      =cut
      --Brent Dax
      There is no sig.

          Ten years from now, at the meeting of the Perl Programming Style Cabal...

        TINPPSC. Shhh.

        --
        The hell with paco, vote for Erudil!
        :wq

Re: GOTO considered (a necessary) evil?
by sedhed (Scribe) on Jul 16, 2002 at 19:20 UTC
    Without getting into the goto issue, may I offer the code I use for tieing a session using Apache::Session::*. It is not a loop at all, therefore no chance of an infinite loop. It makes two tries at a session, once using the supplied ID (if any), and if that fails it tries a second time using undef, to get a new session. Failing that, it dies.
    # $id is either a cookie-passed session ID or undef # $sargs is a hashref of options eval { tie %session, 'Apache::Session::Postgres', $id, $sargs }; if ($@ =~ /^Object does not exist/) { # try to get a new session eval { tie %session, 'Apache::Session::Postgres', undef, $sargs }; die "Can't get new session: $@" if $@; } elsif ($@) { die "Can't get session. ID (if any): '$id' : $@"; }
    cheers!
Re: GOTO considered (a necessary) evil?
by BrentDax (Hermit) on Jul 17, 2002 at 00:50 UTC
    I think that, if you comment appropriately, that goto's meaning is quite clear. Just don't do much more than that with them. :^)

    =cut
    --Brent Dax
    There is no sig.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://181789]
Approved by particle
Front-paged by brianarn
help
Chatterbox?
and the web crawler heard nothing...

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

    No recent polls found