Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Re: where do you put your subs

by demerphq (Chancellor)
on Mar 08, 2002 at 10:10 UTC ( [id://150271]=note: print w/replies, xml ) Need Help??


in reply to where do you put your subs

Actually I strongly strongly disagree with the "put the subs at the bottom" school of thought. (In fact its closely related to the subject that was behind the very first post I ever made at the monastery) I suppose originally this comes from programing systems languages like Pascal and C where you _cannot_ use a function prior to its declaration (contrary to webadepts assertions this has nothing to do with "making you think things through" it has to do with simplifying compiler design, both of these languages can and usually are parsed in a single pass.) In either of these languages you need to place a forward declaration in situations where you have two subs that need to refer to each other.

But when you think it through it makes a lot of sense. You declare variables before they are used so that you (and the compiler) know about them. In fact this is also one of the primary reasons why I still do this in Perl even though I dont need to (I can forget forward decls tho, yah!).

Even in perl legal syntax is different depending on the order of declaration. When a sub is used before it is declared it MUST be referenced with parenthesis ( bar() )or the & (&bar) sigil. It cannot be used in bareword form ( bar ). This alone is enough of a reason to always declare what you use before you use it.

use strict; sub foo{print "Foo\n"} foo; bar; #produces error! sub bar{print "Bar\n"} __END__ Bareword "bar" not allowed while "strict subs" in use at D:\temp\order +.pl line 4. Execution of D:\temp\order.pl aborted due to compilation errors.
And Im sorry to say to those who defend the "subs last" idea, but there are even more serious arguments why putting subs first is a very very good idea. Consider when you write your "main" do you wrap it and all of its variable declarations in a block? If you don't then when you put them at the top of the file _all_ of your subs are now treating your file scoped lexicals as globals. And globals are something to be avoided if at all possible as every good CS student knows. So now this means that you may _think_ that you have written your subs as nice reusable pieces, but in fact they may have subtle dependencies on the "main" that you placed before them, and you wont even know. This is obviously a very bad thing.

Now this is going to sound harsh, in fact there are some that will probably -- me for saying it, but without hearing a very very good justification or seeing a bunch of fiddly stuff (anonymous blocks, BEGIN statements and the like) to resolve the issues above I would usually mark down or consider a programmer to be an amateur/newbie if I see "subs at the end". And if I had to work on the code, the absolute first thing that I would do is reorder the code (and most times when I do it breaks because of the "unintended global" issue) with some very strong comments if the original programmer was near.

Given all of the disadvantages related to "subs last" and the very very few advantages asscoiated I can't see why anyone would do this.

PS:There is even less reason to place use statements at the top of a file than there is to place subs at the top of the file, but its pretty damn confusing figuring out where a subroutine call gets defined if the use statements are scattered all over the place.

PPS:I find it interesting that of the other responders to your post at this point I only agree with one Juerd++ (well, I dont mind mixing code and pod...)

Yves / DeMerphq
--
When to use Prototypes?
Advanced Sorting - GRT - Guttman Rosler Transform

Replies are listed 'Best First'.
Re: Re: where do you put your subs
by Juerd (Abbot) on Mar 08, 2002 at 10:46 UTC
    The lexical/unintented-global problem needs a little more visualization. In the chatterbox, some are really confused about this part.

    # Not using strict, for demonstration. sub at_top { print "Foo is: $foo\n"; } # This is the body of the script, where a head-to-tail flow is in effe +ct my $foo = "I wanted this to be visible only for the body, not all of m +y subs!"; at_top(); # "Foo is: " at_bottom(); # "Foo is: I wanted ..." sub at_bottom { # We're in the same lexical scope!! print "Foo is: $foo\n"; }


    Using strict, but not getting a warning? Yep, because $foo is an "unintended global" (thank you for giving the phenomenon a name, demerphq)
    use strict; my $foo = "blah"; my $bar = "hello, world!"; sub mistake { print $foo; # Works, and prints blah }

    ++ vs lbh qrpbqrq guvf hfvat n ge va Crey :)
    Nabgure bar vs lbh qvq fb jvgubhg ernqvat n znahny svefg.
    -- vs lbh hfrq OFQ pnrfne ;)
        - Whreq
    

Re: Re: where do you put your subs
by derby (Abbot) on Mar 08, 2002 at 13:26 UTC
    demerphq

    Normally I agree with your post but on this one I have to disagree. ++ for a well articulated post, I just don't agree with it. I think your argument hinges on one tiny aspect of perl - using a sub name to invoke it without any syntactic sugar. I like the sugar! I think most perl devs spend too much time playing golf - that sugar helps me. I like the visual clues. When I see bar its hard for me to know what the heck that is but when I see bar() (my pref) or &bar, I instantly know what that is.

    Now for my reasoning on why subs should be after (an implicit or explicit main) - it's easier to read. I love to read - newspapers, magazines, web sites, books, code, books on code. There is a definite well designed, well thought out, and well documented approach to writing - it's called the funnel method where the first paragraph is your general outline (moving from general to hypothesis with each sentence). Succeeding paragraphs are the specifics that support your hypothesis. I strongly believe code should follow this method because coding is just another form of writing and communicating. Think about it.

    Now for you personally, the subs first is great but for someone coming behind you, you're asking them to disavow every other type of writing and succumb to your approach. Much like reading a James Joyce novel, a lot of people will praise you but only a select few will read you the whole way through and even less will emulate you. Hemingway is a better approach - simple, straightforward and very little verbosity. I only ask you to think about those poor slob maintenance programmers who have to come behind you. It's much easier to start at the top and see the general outline of the writing then to skip to the last chapter.

    Being able to read the code is of much greater value than how fast it runs, how few resources it uses, and definetly less than how the compiler or interpreter goes about it's business. Needless to say we definetly cannot reduce down to "See Dick Run" but we can definetly employ a straightforward, almost shaker, style as oppossed to a baroque one.

    -derby

    update: Also, I would never think one was an amatuer for putting sups at the bottom but I would think I was dealing with a pascal convert if I saw them up front (and I would re-arrange it).

      The parens/ampersand are not much of a real problem. The unintended globals are! If you declare a lexical in the body of your script, and you have the subs declared after that, those subs can access the lexical, thus having global-like behaviour (in a way). A very, very good programming concept is to keep things to yourself if you're a piece of code.

      Normal writing does not have the details at the beginning, that's true. But they don't have the conclusion and flow at the beginning! The introduction in writing can be compared to loading modules, using strict. Then, explanation comes, so you can understand the rest of the text. When that's done, body and conclusion follow.

      Subs, in my opinion, are not footnotes, they form information _required_ to understand the rest of the code. That's another good reason for putting small subs that just encode data, and don't have to do much with program flow in separate modules (footnotes...).

      A Perl program is executed in order. Unless you want it otherwise, it will end after having evaluated the last statement, which should be logically the end of the file. And it's a reason to put your subs at the top too! For the same reason you put use statements at the beginning: they are required pieces of knowledge to be able to fully comprehend the rest of the code.

      44696420796F7520732F2F2F65206F
      7220756E7061636B3F202F6D736720
      6D6521203A29202D2D204A75657264
      

        The parens/ampersand are not much of a problem...
        I just wanted to point out that there is a side effect to not using parens on a subroutine/method call: the called subroutine "inherits" the @_ array of the caller. While most of you are probably aware of this it is something that can have some unintended consequences - hence my preference for always using the parens on subroutine calls.

        Michael

      where the first paragraph is your general outline (moving from general to hypothesis with each sentence). Succeeding paragraphs are the specifics that support your hypothesis. I strongly believe code should follow this method because coding is just another form of writing and communicating. Think about it.

      Your analogy doesnt makes sense. The main is in essence the conclusion of an argument. So what you are saying is that when I write an argument I should put the conclusion before the introduction.

      Also, you are talking about aesthetic reasons. I am talking about best practice. Best practice is that which minimizes the chance of bugs and error, not that which reads like a novel. If you want to read nice text then read the comments. The code should be written so as to be as maintainable and error free as possible.

      And if you rearranged my code as you said there is a very good chance it wouldnt run. I do like not using parens when they arent necessary as they often end up occluding the intent in a mess of parens, much as lisp is practically unreadable for most mere mortals. Consider

      print join("\n",map(join(",",map{s/\.//;$_}@$_),@strings); #vs print join "\n",map{join",",map{s/\.//;$_}@$_}@strings;
      Err. maybe not the best example but I know which of the two I find easier to write and to read.

      UPDATE:
      A better example is one that we use all the time, that of finding out how many values are in a hash. Which would you rather use

      my $count=scalar keys %hash; #or $count=scalar(keys(%hash));
      END UPDATE

      BTW: I am a Pascal programmer by background. But that is neither here nor there. In C you cant use a sub until it has been declared either,(Both allow the use of forward declarations, as does perl,) in fact most true compiled languages share this trait.

      Cheers,

      Yves / DeMerphq
      --
      When to use Prototypes?
      Advanced Sorting - GRT - Guttman Rosler Transform

        demerphq

        What a can of worms. When writing with the funnel method, your initial paragraph is an overview of what you're going to do - to me this is just like a main that is nothing more than a driver for meaty subs (who said anything about footnotes?)

        I'm not talking about aesthetics, I'm talking about maintenance. We'll just have to agree to disagree. (And I promise not to re-arrange your code!)

        -derby

      Now for my reasoning on why subs should be after (an implicit or explicit main) - it's easier to read. I love to read - newspapers, magazines, web sites, books, code, books on code. There is a definite well designed, well thought out, and well documented approach to writing - it's called the funnel method where the first paragraph is your general outline (moving from general to hypothesis with each sentence). Succeeding paragraphs are the specifics that support your hypothesis. I strongly believe code should follow this method because coding is just another form of writing and communicating. Think about it.

      I guess that depends whether you're a "top down" or a "bottoms up" programmer.

Re: Re: where do you put your subs
by Trimbach (Curate) on Mar 08, 2002 at 13:10 UTC
    Subs at the top? Subs at the bottom? What's next? Should we discuss whether toilet paper should roll from the top or bottom? :-D Seriously, DeMerphq gives some excellent reasons why you might put your subs at the top, but I've also seen lots of good reasons why you'd want your subs at the bottom. The "unintended global" problem is often a feature, not a bug: yes, I do use global variables (judiciously, and deliberately, and with full knowledge of what I'm doing) and when I do I declare them... at the top of my program. This makes perfect sense to me, as the order of events is:
    use statements my $foo; # LOOK I'M A PACKAGE GLOBAL WOO HOO! main loop/program logic subs
    The subs are all black boxes (mostly, except where they use the package globals), which means (in my mind) it shouldn't make the slightest bit of difference where they appear in code order. They aren't executed "in order" so order becomes immaterial.

    But you know, that's what makes sense to me. Some people are completely allergic to () so they declare their subs first. I don't mind typing "foo()" so I don't care. If you don't like globals (and want to go whatever means necesary to keep from using them, either on purpose or accidentally) hey, more power to ya. I think they're kinda spiffy, and my code is brutally consistent as to how and when I declare them and use them.

    All in all I think perlstyle is silent on this because it really is a matter of taste, and TMTOWTDI.

    Perhaps next we'll discuss proper tab indentation. :-D

    Gary Blackburn
    Trained Killer

      but I've also seen lots of good reasons why you'd want your subs at the bottom.

      Really? But you dont list any.

      The "unintended global" problem is often a feature, not a bug: yes, I do use global variables (judiciously, and deliberately, and with full knowledge of what I'm doing) and when I do I declare them... at the top of my program.

      Then how can you call them "unintended globals"? The point I was making is that a programmer might think that by making lexical declarations in file scoped main that those lexicals are private to the main code. But they _arent_. Whereas what you are talking about is a proper global variable declaration which belongs (if they must be used) as you say at the top. BTW the code snippet you posted has a misnomer

      use statements my $foo; # LOOK I'M A PACKAGE GLOBAL WOO HOO! (wrong) # NO ITS NOT ITS A FILE SCOPED LEXICAL # PRETENDING TO BE A PACKAGE GLOBAL main loop/program logic subs
      The subs are all black boxes (mostly, except where they use the package globals), which means (in my mind) it shouldn't make the slightest bit of difference where they appear in code order. They aren't executed "in order" so order becomes immaterial.

      On face value I agree with you. Properly written it shouldn't matter. But the issue here is best practice. And best practice should be oriented towards whatever minimizes the chance of error, especially anoying hard to trace errors like the 'unintended globals' that I mentioned before. I have no issue if someone wants and specifically uses globals. But shouldnt have to guess that they are globals and that they are intentional. It is for these types of reasons that various rules of thumb (consult tillys homenode and nodes for various lists of them, and a link to good books on this subject) have developed. These rules are not set in stone but are followed by the vast majority of professional programmers because they do minimze the possibility of error in the long run. (Examples would be that code should be indented (heh you mentioned that facetiously and here I am addressing it seriously) between 2 and 8 spaces, with the optimal being 4 spaces, trying to ensure that subroutines are no more than 1 page long, putting short parts of a conditional before the long part.)

      I think that perlstyle avoid this becuase the level of programmer it was intended for is expected to know of these conventions and issues to make an educated decison (as you have, all the credit to you) as to how they wish to procede. Howeve this site has monks of every level of expertise and accordingly it is a responsibility (IMO) of those of us with the expertise to proscribe best practice and not our personal preference. When the newbies know enough to argue they probably wont argue becuase they'll know th reasons why we are saying what we are saying to the newbies...

      Cheers,

      Yves / DeMerphq
      --
      When to use Prototypes?
      Advanced Sorting - GRT - Guttman Rosler Transform

        but I've also seen lots of good reasons why you'd want your subs at the bottom.

        Really? But you dont list any.

        1. It's easier to read. (IMHO)
        2. It's easier to debug (this follows from #1. Again, IMHO)
        3. It matches the way by brain thinks.

        To elaborate, I really like the allusion to programming to writing prose. Compare this:

        #!/usr/bin/perl; a_man_walks_into_a_bar(); man_says("Hello"); bartender_says("What'll ya have?"); man_says("Sasparilla. In a dirty glass"); exit; ### SUBS FOLLOW ### sub a_man_walks_into_a_bar { ... many lines of code } sub man_says { ... many lines of code } sub bartender_says { ... many more lines of code }
        with this...
        #!/usr/bin/perl; sub a_man_walks_into_a_bar { ... many lines of code } sub man_says { ... many lines of code } sub bartender_says { ... many more lines of code } a_man_walks_into_a_bar; man_says("Hello"); bartender_says("What'll ya have?"); man_says("Sasparilla. In a dirty glass"); exit;
        I don't know about you, but in the second example I'm NOT going to read the subs first... I'm going to scroll down (...down down down down) till I get to the good bits. Then I'm going to scroll back up (...up up up) to reference the subs. The subs don't have any context until I've seen the main logic, so I want the main logic (like the main story line) to be the first thing I see.

        This gets worse when you wanna declare a global, which means you'd have to either:

        my $foo; ... zillions of lines of subs ... followed by your main logic
        ...which in itself violates the "always put your variable declarations close to variable usage" rule, or
        ... zillions of lines of subs our $foo; or use vars qw($foo); ... followed by your main logic
        ...which solves that problem, but might have other consequences.
        BTW the code snippet you posted has a misnomer...
        Yeah, you're right. I don't write a lot of code that changes the package in the same file (I prefer separate files for separate packages) so file-scoped-lexicals-declared-at-the-top-of-a-file act virtually identically to package-globals-when-the-package-is-Main. My bad. :-D

        I completely agree that newbie advice ought to be "best practices" but I'm not sure this is one of them, especially when there doesn't seem to be universal accord as to what's right. Even Merlyn puts his subs last in his Web Techniques columns, which are deliberately aimed at instructing others on how to do things the right way. If there's a "best practice" here I think it's "put all your subs together and all your logic together" to prevent some bozo from thinking

        sub foo { ... } program logic sub bar { ... } more program logic
        is a good idea. (Hey, there's probably someone out there like that. :-D)

        Gary Blackburn
        Trained Killer

Re: Re: where do you put your subs
by ignatz (Vicar) on Mar 08, 2002 at 13:15 UTC
    > When a sub is used before it is declared it MUST be
    > referenced with parenthesis ( bar() )or the & (&bar)
    > sigil. It cannot be used in bareword form ( bar ). This
    > alone is enough of a reason to always declare what you
    > use before you use it.

    This alone is reason enough not to. God forbid we clearly seperate methods from scalars. (Sorry. I just never understood the attraction to this Perl quirk.)

    ()-()                                                      ()-()
     \"/    DON'T   BLAME   ME,   I  VOTED   FOR    PACO!       \"/
      `                                                          ` 
    
        Not when dealing with objects.

        my $paco = $foo->bar;
        Is this a scalar or a call to a class method with no paramaters? Let's sift through the code to find out, oh joy!

        My main point is that subroutines are doing the majority of the work in a block of code. () at the end makes them stick out. Hi there! Being almost blind, I love having these handy little pointers in the code that I'm looking at. The fact that it the rule of law in other languages means that my subconsious is naturally drawn to them. Just because you can do something different than other languages doesn't mean that you have to.

        update: I just knew that I would put my foot in my mouth when I start talking about OO perl. ++ to both responses. (I still hate it bareword methods. :-P)

Re: Re: where do you put your subs
by rjray (Chaplain) on Mar 08, 2002 at 22:28 UTC

    First, let me say that I gave this node a ++ despite the fact that I'm about to disagree with most of it, and take offense at some part of it. But you make your arguments in a clear, readable fashion :-).

    That having been said, I consistently put subroutines after their first use. Except in a few cases where the cross-referencing was so thorough that I resorted to alphabetical ordering1 for lack of a better scheme. Even then, the subroutines come after the main logic. In a later node, you tweaked someone for saying that there were good reasons, but that he hadn't offered any. Here are some of mine.

    The flow of thought tends from the least-specific to the most specific. I am first interested in knowing that the program opens a file, operates on the content, and then outputs a report. From that point, I will (depending on what I'm trying to learn-- am I debugging this? Am I adding new features?) choose to delve more deeply into what type of processing, exactly, is being done. Now, that's probably in a subroutine somewhere. And from where I'm at in the source code, it's not important whether the sub is above me or below me. But I really don't want to scroll through several pages of subroutines to finally find the main logical loop, and then find that I only need to look at one subroutine which, by the way, I passed about 3 pages ago. I don't generally know what subroutine I care about when I first start reviewing code. After that, when I know a specific name, the placement is irrelevant-- I'm going to /-search in vi or Ctrl-S in XEmacs, depending on what's available to me.

    Forget not that there is a use subs (); just as there is a use vars (); pragma. I try to pre-declare all my subroutines, because I generally plan before I start banging out code anyway. I want to know within the first 15-20 lines of code what all the subroutines and package-global variables are in a program or module. I should be able to get that much information with a simple head invocation. Again, where the subs themselves are doesn't even matter here. If you don't use the subs pragma and do all your declaration management by ordering, I'm going to have to read all of your code to find out what is there. Think you would gripe at me if you had to read my code with the subs at the end? If you're one of my assigned engineers and you bring me code for review that has to be read all the way through just to know what subroutines are defined and declared, we'll be having a conversation.

    Want more? How about this: The end of the file in a Perl script is not the same as the end of the program. What about modules that use AutoLoader2, or scripts that use SelfLoader3? Scripts often embed data (other than subroutines) after __END__ or __DATA__. In those cases, putting subroutines "at the end" isn't even technically possible. Worse, if the actual logical loop of the program is sandwiched somewhere between subroutine blocks and whatever follows __END__, how much more intuitive is that to the reader?.

    What something like this ultimately comes down to, is in fact an evaluation of how well-designed your code is (or was) before typing began. Consistency in style is more important than adherence to a specific style. As programmers, we will always be exposed to styles different than our own. Whether or not we can read them and derive the information we seek from the code, that is what we need to judge the programmer against.

    And lastly, show some caution when making heavily-generalizing statements like, 'I would usually mark down or consider a programmer to be an amateur/newbie if I see "subs at the end"'. I do take offence at it, and I imagine there are other Perl coders who would, as well. Constructs like:

    exit main(@ARGV);

    only serve to make Perl code look more C-like. But C is not a scripting language, and everything in C, including main, is explicitly a subroutine. You don't have a situation in which a C program will start at the first execution instruction, thus requiring you to wrest control avay from the errant Program Counter and direct it to where you want it. Instead, a linked C program includes code that starts execution at the linker-provide entry point for main()4.

    I've been programming Perl as a full-time job for almost 10 years, well before 5.000. When I write C, it looks like C. When I write Tcl or Lisp, that's what the code looks like. And my Perl looks like Perl, surprisingly enough.

    --rjray

    1 Yes, I have done this once or twice, only when there was no other clear and obvious way to order them. If the order is going to be arbitrary anyway, then it at least helps to know that read_file will come somewhere before report_error.

    2 What I Want, When I Want It: Using AutoLoader and AutoSplit. Originally in The Perl Journal Issue #6.

    3 Ibid.

    4 In truth, a lot of stuff generally happens before the actual entry into the main routine, none of which is really relevant to the argument at hand.

Log In?
Username:
Password:

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

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

    No recent polls found