Search code examples
perllabelsubroutinegoto

"Can't goto subroutine outside a subroutine"


I've created a subroutine named "MENU" with a label named "INIT_MENU" at the top of the subroutine, but when i call this label i got a error : Can't goto subroutine outside a subroutine at program.pl line 15

Here is an example:

sub MENU {INIT_MENU: print "blah blah";}

and here is the line 15:

goto &MENU, INIT_MENU;

Sorry if it is a duplicated question, i searched in all possible places and even in the official site of Perl


Solution

  • The goto expects a single argument, so this code first executes goto &MENU and then after the comma operator it evaluates the constant INIT_MENU in a void context (drawing a warning).

    The purpose of goto &NAME is to replace the subroutine in which it is encountered with subroutine NAME; it doesn't make sense calling it outside of a subroutine and this is an error. From perldiag

    • Can't goto subroutine outside a subroutine

    (F) The deeply magical "goto subroutine" call can only replace one subroutine call for another. It can't manufacture one out of whole cloth. In general you should be calling it out of only an AUTOLOAD routine anyway. See goto.


    A comment on the purpose of this.

    A lot has been written on the harmfulness of goto LABEL. Not to mention that INIT_MENU: cannot be found when hidden inside a routine. The upshot is that there are always better ways.

    A sampler

    • Call the function and pass the parameter

      MENU(INIT_MENU);
      
      sub MENU {
          my $menu = shift;
          if ($menu eq 'INIT_MENU') { ... }
          ...
      }
      
    • If, for some reason, you want to 'hide' the call to MENU use goto &MENU as intended

      sub top_level {   # pass INIT_MENU to this sub
          ...
          goto &MENU;   # also passes its @_ as it is at this point
      
          # the control doesn't return here; MENU took place of this sub
      }
      

      This altogether replaces top_level() with MENU() and passes on its @_ to it. Then process input in MENU as above (for example) to trigger the chosen block. After this "not even caller will be able to tell that this routine was called first," see goto. Normally unneeded

    • Why even have MENU()? Instead, have menu options in separate subs and their references can be values in a hash where keys are names of options. So you'd have a dispatch table and after the logic (or user choice) selects a menu item its code is run directly

      my %do_menu_item = ( INIT_MENU => sub { code for INIT_MENU }, ... );
      ...
      $do_menu_item{$item_name}->();
      
    • Menu systems tend to grow bigger and more complex with development. It would make sense to adopt OO from start, and then there are yet other approaches for this detail

    If you find yourself considering goto it may be time to reconsider (some of) the design.


    See for instance this post and this post. It's (even) worse than that in Perl since there are specific constructs (and restrictions) for situations where goto can be argued acceptable, so there is even less room for it. One example is jumping out of nested loops: there are labels in Perl.