Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Would you use 'goto' here?

by Ovid (Cardinal)
on Dec 06, 2001 at 03:06 UTC ( [id://129788]=perlquestion: print w/replies, xml ) Need Help??

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

Recently, I asked tilly under what circumstances he would use a goto, since I have only used it once in all of my Perl programming. Oddly enough, I've found a circumstance under which it seems perfectly reasonable, but somehow, it doesn't feel quite right and I'd like to hear pros and cons about this.

In my HTML forms, I have "action" and "type" parameters. In the code snippet below, "action" equals "edit" and "type" equals "product". This allows someone to, duh, edit a product. However, if an office ID is sent, then I know that they are actually editing product information for an office. Since subroutine calls are made based upon a dispatch table that checks the action and type, calls to edit an product and edit an office product automatically go to the same subroutine (they are the same permission, as defined in the business rules). As a result, I considered using a goto to transfer control to the correct subroutine if I see that they're trying to edit a product for an office:

sub edit_product { my ( $query, $db, $mod, $sec ) = @_; if ( $query->param( 'officeID' ) ) { goto &edit_office_product }; $query->delete( 'action' ); $query->delete( 'type' ); if ( $query->param ) { # they've submitted the form } else { # send them to the form } }

I could simply use the following:

if ( $query->param( 'officeID' ) ) { return edit_office_product( @_ ); }

I have at least nine functions that this decision will affect and I know that this seems really nitpicky, but I was wondering, as a matter of style, what people would prefer. I have some error reporting which checks the caller and I think that the goto would be a cleaner solution.

Cheers,
Ovid

Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

stats.

Replies are listed 'Best First'.
Re: Would you use 'goto' here?
by Masem (Monsignor) on Dec 06, 2001 at 03:31 UTC
    I think the fact that you had to explain the special form in your errata to the original message is a good enough reason. The normal concept of a goto statement should be understood, save for syntax, by any programmer. However, the form of goto that you are using here isn't consistent with that, and would require a good reading of the docs to understand the implications of using it. Functionally, it does exactly what you want, but realistically, will other programmers that might look at this code understand it?

    That said, this might be a cool use for it, but as you also point out, I think the non-goto form is easier to see and understand. In addition, if you are talking dispatch tables and logic otherwise, I would try to improve the logic of the dispatch tables and thus avoid trying to make that second dispatch function call inside the first dispatch call.

    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
    "I can see my house from here!"
    It's not what you know, but knowing how to find it if you don't know that's important

Re: Would you use 'goto' here?
by runrig (Abbot) on Dec 06, 2001 at 03:20 UTC
    If you want to change the apparent caller, then by all means use goto (since it's to a code ref and not to a label). Though I might do:
    goto &edit_office_product if $query->param( 'officeID' );
    Though I think I might find it confusing if I'm looking at your error reporting which says 'edit_office_product()' was called from, e.g., foo() and I can't find any reference to edit_office_product in foo() because it was really called from edit_product() (don't know if this is the case from what you said). In which case I might do:
    return edit_office_product(@_) if $query->param('officeID');
Re: Would you use 'goto' here?
by tadman (Prior) on Dec 06, 2001 at 03:24 UTC
    I'd never seen the goto &LABEL format either, so this is quite interesting. This ability to do with functions what exec does with processes is certainly remarkable, but why it was called "goto" is not entirely clear. Some people, myself included, loathe that particular four letter word.

    Just for the sake of clarity, it is probably best to use the return method you suggest.

    As a note, if you find this sort of thing cropping up in nine different places, maybe you should switch to an OO framework. Override the edit method for Product::Office which @ISA Product:
    my $product = new Product; $product->edit(); # Same as edit_product my $office_product = new Product::Office; $office_product->edit(); # Same as edit_product_office
    I've seen some very clever uses of goto in extremely optimized code, where backing out of a pile of loops is just too costly, but this is very rare. Besides, if you were concerned about ultimate performance, you wouldn't be using Perl anyway.
(Ovid) Re: Would you use 'goto' here?
by Ovid (Cardinal) on Dec 06, 2001 at 03:09 UTC

    Ugh! I forgot to explain this version of goto. From the docs:

    The "goto-&NAME" form is quite different from the other forms of "goto". In fact, it isn't a goto in the normal sense at all, and doesn't have the stigma associated with other gotos. Instead, it substitutes a call to the named subroutine for the currently running subroutine. This is used by "AUTOLOAD" subroutines that wish to load another subroutine and then pretend that the other subroutine had been called in the first place (except that any modifications to "@_" in the current subroutine are propagated to the other subroutine.) After the "goto", not even "caller" will be able to tell that this routine was called first.

    In other words, it's like the current subroutine doesn't exist. As an editor, I could have edited my parent node to include this, but that seemed rather abusive of that right :(

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

      Clearly, the "goto-&NAME" form was intended for use by the low-level "magic" (as perrin so aptly put it in Re: Would you use 'goto' here?) of AUTOLOAD, which, just as clearly, is a dispatch function. Since you're also using it for dispatching, it seems on the one hand appropriate.

      On the other hand, quite a few responses to this node (in particular, Masem's, in which he says, "I think the fact that you had to explain the special form in your errata to the original message is a good enough reason") seem to indicate that this form of goto may not be widely used enough to consider it an "idiom" (in contrast with some other widely used constructs nevertheless frequently misunderstood by experienced C/C++/Java programmers new to Perl).

      If your goal, as you say, is to "optimize for correctness and clarity", then this might not be the ideal solution. How important is the call stack in a production app anyway? Isn't that more to do with debugging than with normal usage?

      IMHO, you're much better off switching to:

      return edit_office_product(@_) if $query->param('officeID');

      as first suggested by runrig in Re: Would you use 'goto' here? in this thread.

      dmm

      
      You can give a man a fish and feed him for a day ...
      Or, you can teach him to fish and feed him for a lifetime
      
Re: Would you use 'goto' here?
by dws (Chancellor) on Dec 06, 2001 at 03:42 UTC
    In this case the performance gained from using the goto instead of a more conventional subroutine call is probably outweighed by the maintenance risk. Somebody is liable to pick this up and not understand it, and then either undo the trick or add a layer of bandaids on top of it. I'd go with
    return edit_office_product(@_) if $query->param('officeID');

      Actually, my concern was having the call stack accurately reflect what I wanted (and having a function do what I truly want it to do) versus having a bit of code that some might not understand. As a standing rule, I never optimize for performance. I optimize for correctness and clarity. Performance is rarely an issue for an unfinished product as it can't truly be predicted :)

      Cheers,
      Ovid

      Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: Would you use 'goto' here?
by perrin (Chancellor) on Dec 06, 2001 at 03:43 UTC
    I can't see any reason to use goto rather than just calling another sub here. It makes sense in the AUTOLOAD case you mentioned because it helps hide something that is basically low-level magic. Here you just have a business rule about doing things one way if calling with certain params and a different way with others. I would certainly want to see that reflected in your strack traces and error messages!
Re: Would you use 'goto' here?
by clintp (Curate) on Dec 06, 2001 at 06:17 UTC
    Personally, I have no problems with it. (As a matter of fact, I posted something in the Snippets section earlier today that used the "magical" goto.)

    The overall taboo associated with an unconditional jump instruction (goto) is just plain silly. If a tool is available, by all means use it if it does the job well. Sure with TMTOWTDI means that we probably don't need goto in Perl. We could also get away without grep, map, foreach, my, local, readline, for, and hashes if those suddenly became taboo also.

    As for the criticism that a later programmer might not understand the code: limiting yourself to widely-known and well-used syntax is the antithesis of Perl programming and I want no part of it. goto is well documented, and it quite plainly states what the goto & statement does. Just because this isn't what OTHER languages do with goto isn't a reason to avoid it. Other languages might use "write" as the standard output statement -- perl doesn't. <>'s are exclusively comparison operators in some other languages -- that doesn't mean we should avoid them in Perl.

      As for the criticism that a later programmer might not understand the code: limiting yourself to widely-known and well-used syntax is the antithesis of Perl programming and I want no part of it.

      But using obscure syntax when obvious and well-known syntax (a subroutine call) exists is the antithesis of maintainable code. There are so many ways to do things in Perl that you have to choose what you want to optimize for. Ovid says he wants to optimize for clarity and maintainability. That sometimes means doing things in a simpler way and avoiding cool but obscure (though documented) tricks like this.

        No. No. NO. NO. NO!

        A language has a certain syntax. Knowing that syntax is the responsability of the person who says "I know language XXX". It is not my responsability to make sure that I dumb down my code for your inability or unwillingness to keep your end of the bargain!

        I'm currently in a shop where use of $_, map, and grep is nominally discouraged. Why? Because others might not understand the code later down the line. (Yet, this place uses the most complex regexes I've ever seen, making me go to my book over and over.)

        If I was in C, would you discourage bit-masking? What about pointer arithmetic? Yes, they can cause problems, but they're a feature, not an 'oops'!

        Let me take another tack. Let's say you told me "I can read English". So, I write you a letter in English. No problems, right? Well, you start complaining about my use of the words with more than 6 letters. They're too difficult. You've never seen them. You don't like them. (And, yes, I've received this complaint before!) Should I not use a standard part of the language you have said you speak solely because you don't want to make good your assertion?!?

        I didn't think so.

        ------
        We are the carpenters and bricklayers of the Information Age.

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        I couldn't possibly disagree more.

        Just so everyone understands that you're not just afraid of goto, let's take a look at a few pieces of syntax:

        • while(<FH>) assignment to $_
        • { local $/; $a=<SLURP>; }
        • sub cntr { my $r=0; return sub { $r++ }; }
        • $a="aaaa"; $a++
        • map {...} sort {...} map {... }
        • grep { s/foo/bar/ } @arr
        • *copy=\$original;
        Are these pieces of code obscure (though documented) tricks? Arguably, to someone new to Perl they're all tricks. Most of these can't be attempted (in their current form) in C, Shell, Java, Pascal, REXX, FORTRAN, or Basic without throwing a lot more code at the problem. So a programmer with 25 years of fairly broad experience could claim that these are all witchcraft of the worst sort and insist you avoid them. (All of these have an alternate "workaround" in Perl that could be used for "clarity.")

        But, in fact, they're all fairly idiomatic and common Perl. One programmer's "trick" is another programmer's tool. When does a "trick" become an idiom?

        I'd use any of them in a heartbeat if it was the appropriate tool for the job. I would hope you would too. Coding for clarity is one thing, wearing shackles is quite another. If the syntax is there, documented, and not deprecated -- use it.

        Those that fear and are unable to deal with large languages should stick to C.

      Just because this isn't what OTHER languages do with goto isn't a reason to avoid it.

      Bravo! My thoughts exactly!

      Yves / DeMerphq
      --
      This space for rent.

      IMNSHO throwing around the word silly as a way of saying, don't listen to them is stupid.

      There is a reason for the taboo. And anyone who cannot accurately state, in their own words, on demand, either succinctly or verbosely as requested, what the reasons are for the taboo has no business using the construct.

      I am not exaggerating.

      Please follow the link that Ovid had to his question, and my answer, about when goto is justified. As you will see there, my position about goto is not religious. There are cases where it is justified and justifiable. However without a complete understanding of why the construct is problematic you will simply not have the tools to recognize when its utility transcends the issues associated.

      This is one case where, while I like having people really understand what is going on, I would prefer that people avoid goto for cargo-cult reasons rather than trying to think up reasons to use it.

Re: Would you use 'goto' here?
by chipmunk (Parson) on Dec 06, 2001 at 05:54 UTC
    I'm curious why it is that you don't have the dispatch code check the office id, as well as the action and type, so that you can call the desired subroutine directly.

    I think that it's just as well not to use goto in this case.

      Just to give you a taste of what I am doing and why, here's a (heavily edited) version of the dispatch code.

      my %page_control = ( edit => { product => { page => 'ps-main-product-edit.tmpl', function => \&edit_product }, }, default => { page => 'ps-main.tmpl', function => '' } ); # not worried about auto-vivification because false entries will g +o to defaults my ( $page, $function ); if ( $action and $type ) { $page = $page_control{ $action }{ $type }{ page }; $function = $page_control{ $action }{ $type }{ function }; } $page ||= $page_control{ default }{ page } my $permissions = $sec->get_permissions( $section ); if ( $action and $page =~ /^$secured/ ) { if ( exists $page_control{ $action } and exists $page_control{ $action }{ $permType } and ! $permissions->{ $type }{ $action } ) { my $article = $type =~ /^[aeiouAEIOU]/ ? 'an' : 'a'; $sec->force_logout( "$user tried to $action $article $type +." ); } } # call correct function my ( $template_data, $new_page ) = $function->( $query, $db, $mod, + $sec ) if defined $function and $function; $page = $new_page if defined $new_page and $new_page; $template->process( $page, $template_data ) or dienice $template-> +error();

      Basically, I use the hash to assign pages and functions for each action (permission) and type (section). We then call the function with all the data that it will need and it returns template data and, if necessary, a new page to send the user to. All functions have the same inputs and outputs.

      The problem, as I see it, is how the permissions model is set up to prevent people from gaining access to something that they shouldn't. If I start building in a bunch of special cases into the dispatch code, I'm concerned that now, or in maintenance, we'll overlook something in the security portion. Since that's relatively stable, I really want all changes to occur after security has been nicely wrapped up.

      Cheers,
      Ovid

      Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: Would you use 'goto' here?
by atcroft (Abbot) on Dec 06, 2001 at 06:40 UTC

    It is important to note that the statement Djikstra made regarding GOTOs was in the late 1960s-a result of studying numerous programs where the use of GOTOs, combined with the relative lack of programming structures, made the code hard to follow, error-prone, and difficult and costly to maintain.

    The statement that GOTOs are bad is now taken almost as dogma, but what must be kept in mind is this: if the use of it improves whatever metric you choose to look at, be it read/maintainability in code that must be maintained in production for a lengthy period of time or efficiency in an application where performance is a major issue, then by all means the use of it should be allowed.

    Remember that another solution may also exist, though, and may be preferrable, especially if style or style conflicts are an issue, others who must assist in maintaining code have difficulties from their use, or if their use starts to become abusive.

      It is important to note that the statement Djikstra made regarding GOTOs was in the late 1960s-a result of studying numerous programs where the use of GOTOs, combined with the relative lack of programming structures, made the code hard to follow, error-prone, and difficult and costly to maintain
      Has anyone gone back and re-done this study in the last 30 years? Are we just repeating things that our forerunners thought was true because we're too lazy to go back and check our facts? Might be worth study.

Re: Would you use 'goto' here?
by demerphq (Chancellor) on Dec 06, 2001 at 15:51 UTC
    Considering that one of your objectives is to control what caller() reports then I think that using goto &sub is prefectly legitimate, after all that was the very reason why it is included in the language at all.

    However to address the readability/maintainability of your code I think that perhaps you are taking the wrong approach, a slight change in methodology would IMO make it much more clear. Instead of having one sub that handles both the forwarding and the processing of one case, use one sub that forwards to two others. Give it a nice convienient, obvious name and presto, you get the magic with the readability:

    sub edit_product_disptacher { # uses magic goto to fool caller() my ($query)=@_; if ( $query->param( 'officeID' ) ) { goto &edit_office_product } else { goto &edit_product } }
    It seems to me that documenting that you are using a magic goto and why should be sufficient for and support programmer. It tells them what you are doing (so they can easily find the docmentation) and why, and once they read the docs they will (er, should :-) understand perfectly whats going on.

    Yves / DeMerphq
    --
    This space for rent.

Re: Would you use 'goto' here?
by Fletch (Bishop) on Dec 06, 2001 at 07:03 UTC

    You could always use a hash of coderefs to make a jump table.

    my %query_to_sub = ( officeID => \&edit_office_product, fnord => \&vreemflitzel ); $query_to_sub{ $query->param( 'spoo' ) }->()

    Or if you've got a module, just call the apropriate method on it (if it can()).

    my $method = $foo->can( $query->param( 'behavior' ) ); if( defined( $method ) ) { $foo->$method() } else { do_error( "Unknown method " . $query->param( 'behavior' ) ); }

    Of course if you're going to get OOPy this would be something you could use a State/Strategy to represent the behavior (which reminds me I really should write a review of Design Patterns and Refactoring like I've been meaning to for a while now . . .).

Re: Would you use 'goto' here?
by hsmyers (Canon) on Dec 06, 2001 at 23:59 UTC

    For those who are interested, here is a link to one of the reasons behind the cargo cult notion of no gotos. This of course is “Go To Statement Considered Harmful”, E. Dijkstra's famous letter to the editor of Communications of the ACM, Vol. 9, No. 5 (May 1966), pp. 366-71. Like the basis of many such things, it doesn't quite say what many think—and in any event, should be read by anyone who programs or even pretends to program!

    Another article that should be read in this context is found here. Actually the link is to a .pdf file created from scanner input. But still legible for all of that. This paper, “Structured Programming with go to Statements”, is by Donald Knuth, published in Current Trends in Programming Methodology, Vol. 1, Raymond T. Yeh, ed., New York, NY., 1977. In it Knuth details a history of the go to discussion and then follows with his position and rationale.

    This pair make a good start, but only a start. If you want more before you come to an informed opinion, try Classics in Software Engineering Editied by Edward Nash Yourdon, New York, NY., Yourdon Press, 1979. While sadly out of print, this shows up from time to time at Powell's Technical in Portland. Try http://www.powells.com/technicalbooks

    –hsm

    p.s. Do I use gotos? Of course I do, I'm an assembler hack—can't get there from here without them!
      Thank you for the link to Knuth's paper.

      I have read about it before, but not read it. As normally happens, there are insights in the original that are forgotten later. One of the best ones so far for me is the note on p277 which points to a proof by Rao Kosaraju that implies that the normal structured programming constructs, with loop control, with named loops is sufficient to replace any algorithm using gotos with an equivalent one without goto which does no extra computation. (The key point being no extra computation.) I find this an interesting theoretical validation of the point that loop control and named loops in Perl is sufficient to replace the vast majority of reasonable gotos in other languages.

      And if you go on to p282 you will find the following quote:

      Certain go to statements which arise in connection with well-understood transformations are acceptable, provided that the program documentation explains what the transformation was.
      The example that he demonstrated this with was taking an obvious recursive algorithm, recognizing tail-recursion, and rewriting it for efficiency. This involved introducing gotos. In my note to Ovid above I used the examples of implementing a finite state machine, and TheDamian's Switch.

      And then there are the comments which are fascinating in hindsight. For instance the one on p295 about how a given level of abstraction often defines several related routines and data definitions. His example is how the representation of a table is tied to the routines you need for storing to and fetching from it. His assertion that the next generation of languages will probably take into account such related routines seems to me to be an excellent anticipation of OO programming...

      And his closing note about the future seems to be an excellent description of the present. Higher level languages like Perl often contain goto, but people just don't see the need to use it, and so the issue faces away. However, as he said, it remains true that if you let beginners know about goto and leave them with the opinion that it is OK to use the feature, they will discover many cases where they "need" goto, but the cause of the need is poor planning on their part, and they would be better off hitting the roadblock, backing off, and then rethinking rather than working their way forward into more trouble by adding gotos...

Re: Would you use 'goto' here?
by ask (Pilgrim) on Dec 06, 2001 at 07:20 UTC
    Once in a while I've used something like
    if (exists $commands{$command}) { my ($result) = eval "&$command"; ... }
    to avoid the if ($command eq "foo") { &foo } elsif (...) thing. I think I picked it up from Jim Winstead

     - ask

    -- 
    ask bjoern hansen, http://ask.netcetera.dk/   !try; do();
    
      eval string, from a mod_perl guy! I'd be shocked, if I didn't think you had a good reason. :)

      I'd still prefer subrefs, can, and maybe even some symbolic references with map:

      %commands = map { $_ => \&{ $_ } } keys %commands;

      Season to taste.

      Update: Not that eval is slow, but that eval string has some memory leaks not fixed until 5.8. That can be nasty in mod_perl.

        In a program that'll fork and run another program, do multiple dns lookups, open and read a handful of files, write to the network, wait for the network and much more? Sure, we can eval half the day1 and it won't make much difference if any at all.

         - ask

        1) No, of course not literally half the day. :-)

        -- 
        ask bjoern hansen, http://ask.netcetera.dk/   !try; do();
        
      Is there any reason why string eval is preferable to either one of these:
      { no strict 'refs'; $command->(); # OR &$command(); }

      -Blake

Re (tilly) 1: Would you use 'goto' here?
by tilly (Archbishop) on Dec 06, 2001 at 20:53 UTC
    No, I would not.

    It sounds like you have a top-level dispatch table, but (like a tree) you have a lower-level set of dispatches that need to happen on some nodes on specialized information. And what you are doing is handling special cases with a goto which will make it harder later to figure out, "How did I get here?" so that some subroutines can do double-duty.

    Instead I would operate as follows:

    sub dispatch_edit_product { my $query = $_[0]; if ($query->param("officeID")) { edit_office_product(@_); } else { edit_generic_product(@_); } } sub edit_generic_product { my ( $query, $db, $mod, $sec ) = @_; # ... } sub edit_office_product { # ... }
    And now you leave complete call information for your debugging routine to report, and every function in the call stack makes perfect sense.
Re: Would you use 'goto' here?
by belg4mit (Prior) on Dec 06, 2001 at 06:33 UTC
    I myself wouldn't. The only time I can recall ever using goto is in my recent cron script. As a means of effectively restarting the program.

    To me this really seems to be a case for using if/else contructs. One question though, why aren't you checking for the officeID outside of the sub? Since you are apparently checking other things before to determine which sub to call...

    --
    perl -p -e "s/(?:\w);([st])/'\$1/mg"

OT: Re: Would you use 'goto' here?
by Reverend Phil (Pilgrim) on Dec 06, 2001 at 22:30 UTC

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (5)
As of 2024-03-19 07:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found