Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

When to use Prototypes?

by demerphq (Chancellor)
on Nov 09, 2001 at 16:23 UTC ( [id://124339]=perlmeditation: print w/replies, xml ) Need Help??

Recently I've had a few conversations regarding prototypes and when or if they should be used, then today I saw a post by John M. Dlugosz where he used them for as far as I could tell for no good reason. (Which should not be read that he had no reason, just that I couldn't figure it out. :-)

My thinking about protypes is that basically they are there to resolve unusal situations and should not be used except with extreme caution and very strong justification, but I have to admit that my thinking in this regard has been strongly influenced by Far More Than Everything You've Ever Wanted to Know about Prototypes in Perl by Tom Christiansen.

My feelings are pretty well summed up by two paragraphs from Christiansen's article

The major issue with ``prototypes'' in Perl is that the experienced programmer comes to Perl bearing a pre-conceived but nevertheless completely rational notion of what a ``prototype'' is and how it works, yet this notion has shockingly little in common with what they really do in Perl.

Which justifies my gut feeling that most time people use them they arent actually doing what the user thought and accoridingly are dangerous, both to the user and to the original programmer.

These mostly do nothing more that provide an implicit context coercion in order to spare the caller from having to sprinkle the code with calls to scalar() or to supply backslashes in order to pass aggregates by reference. They do comparatively little in the way of checking the type or number of arguments. So just what good are they, anyway?

Which even though I have read Christiansens article, and understand it as well, leads me to wonder why John used it and why and when the other monks use it, in fact Im curious to hear in general what the monastery thinks of prototypes and their use.

Yves / DeMerphq
--
Have you registered your Name Space?

Replies are listed 'Best First'.
Re (tilly) 1: When to use Prototypes?
by tilly (Archbishop) on Nov 09, 2001 at 17:19 UTC
    As you well know, my answer to when to use prototypes is generally, Don't!

    And an example that I would point to of what the bugs caused by prototypes working as designed (rather than by their being buggy) look like in practice, take a look at printf vs. sprintf. How quickly could you, or more importantly a co-worker of yours, figure out what went wrong there? Why would you want to subject yourself to going wrong like that when you have a choice?

    On whether prototypes are a bad idea, I don't think they are in a language where they do something reasonable. But they don't in Perl.

      ...or at least in Perl 5.

      As pointed out in Apocolypse 2 (where's the danged thing on perl.com??) or Exegensis 2, perl 6 will have much better prototyping featurs, such that you can have something like:

      # PERL 6!!!! @array1 = ( 1..5 ); @array2 = ( 6..10 ); do_array_magic( @array1, @array2 ); #... #... sub do_array_magic ( ARRAY $a, ARRAY $b ) { # @a is [ 1,2,3,4,5] # @b is [ 6,7,8,9,10 ] }
      instead of
      # PERL 5 @array1 = ( 1..5 ); @array2 = ( 6..10 ); do_array_magic( @array1, @array2 ); #... #... sub do_array_magic { (@a, @b) = @_; # @a is [1..10] # @b is empty. }

      But prototyping in p5 is mearly a pain, and even if you do check the args by prototype, you still probably need a block of warn or die statements as you check arguments for validity at the start of the sub. So they mearly get in the way instead of being useful currently.

      -----------------------------------------------------
      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

(tye)Re: When to use Prototypes?
by tye (Sage) on Nov 09, 2001 at 21:17 UTC

    Looking at the post in question, I'll guess that John is using prototypes to validate the number of arguments passed into each sub at compile time. I can certainly understand the desire to do that. I've even done that in the past. But I found that the misfeatures of Perl 5 prototypes outweighed this desirable effect (see the reference article for the full story on those misfeatures).

    Now I only use prototypes for making compile-time constants and to emulate map's and grep's ability to take bare blocks as code references (and this latter use is quite rare and really only gives a very minor benefit of not having to see "sub" in front of each block).

    Making compile-time constants is really the only use of prototypes that I can recommend.

    BTW, I check the arguments passed into subroutines rather thoroughly, inside the subroutine, at run time. And I prefer to have test suites that validate the code paths so that this validation is effective before going into production, even though the checking isn't done at compile time. Since Perl uses a lot of late binding, checking subroutine arguments at compile time often just isn't possible.

            - tye (but my friends call me "Tye")
      Now I only use prototypes for making compile-time constants and to emulate map's and grep's ability to take bare blocks as code references (and this latter use is quite rare and really only gives a very minor benefit of not having to see "sub" in front of each block).

      Yeah the bare block trick was what I meant by 'unusual situations' :-) A quick question (which I could answer myself after a bit of testing, but it seems like a point that should be shared) doesn't it also coerce a statement into a block as well?

      My second question is how do prototypes help you with compile time constants? I assume you are talking about something like

      sub LOW_PRE_PI {3.141}
      and I dont see where prototypes come into play. Am I missing something?

      Yves / DeMerphq
      --
      Have you registered your Name Space?

        When you use an empty prototype (no args) for constants, the parser then knows not to interpret anything that follows as arguments for to that sub:

        sub LOW_PRE_PI {3.141} sub HI_POST_PI () {3.142} my @n_proto = (1, LOW_PRE_PI + 5, 11, 15); my @y_proto = (1, HI_POST_PI + 5, 11, 15); print "@n_proto\n@y_proto"; __END__ Output: 1 3.141 1 8.142 11 15

        In the first case, everything following the unprototyped version is taken as an argument list for the subroutine.

        Yesterday lestrrat showed how use constant $DEBUG works its magic Although he didn't specifically point out the bare prototype, it is there in the Deparsed output...

        -Blake

      tye says "And I prefer to have test suites that validate the code paths so that this validation is effective before going into production, even though the checking isn't done at compile time."

      This sounds like a good idea, but I'm not sure that I entirely understand it. Do you mean that you systematically exercise all of the possible paths through your program? What's a good way to actually do this in Perl?

      More generally most of the modules that I've looked at have very specific tests, running a function or two from the module on defined data. This is also a good idea, and it's what I've (slavishly) copied in my own work. Could some experienced person point me ot resources on more albaorate testing of Perl software? I'm personally especially interested in database driven web sites with CGI.pm etc.

      -- Anthony Staines
Re: When to use Prototypes?
by hsmyers (Canon) on Nov 09, 2001 at 18:36 UTC
    I don't use them for reasons similar to those given. It's a problem that (I think) comes from overloading a concept. Like overloaded operators…imagine code in C++ or Ada where the '+' sign performed integer division or the like. Concepts don't exist in a vacuum, so when a variation on a 'well known' idea turns out to be counter-intuitive then it might as well be a bug. Or perhaps a retro-grade feature!

    hsm

Re: When to use Prototypes?
by Rex(Wrecks) (Curate) on Nov 10, 2001 at 00:56 UTC
    I think I'm about to lose my newly earned 'monk' status with this post, but here goes anyway.

    I use prototypes for any sub I write that does not take a scalar(s) as args. I do this for 2 reasons:
  • First, it helps for readability of larger scripts, and just by looking at my sub prototypes at the top I know what to pass it.
  • Second, for maintainability, when I look at stuff I wrote months ago I can see exactly what I'm passing and how to reuse that sub easily.
    Now this could also be because I write too much C, and like to see what is being passed around at a glance, personally, prototyping helps. Being able to pass an array ref (like you have to) in the same way I would (logically) pass an array (like I would want to) is also more readable for me.

    "Nothing is sure but death and taxes" I say combine the two and its death to all taxes!
      I don't think it does much for readability, since at best you see the projected type of argument, but still have to look the meaning of each of them. Putting a cleanly written and commented local variable assignment like my ($ip, $port, $use_udp, $msg_array) = @_; at the top of a function will probably help a lot more.
Re: When to use Prototypes?
by John M. Dlugosz (Monsignor) on Nov 10, 2001 at 01:47 UTC
    FWIW, my reason for using prototypes in a situation like this:
    sub helper ($$$) { ... sub foo { # code that calls helper
    is because it does check the prototypes, since it's not a member dispatch or anything like that.

    Specifically, if I add a parameter to the helper (or remove one) as I tinker with the code, the compiler will catch any uses I didn't update.

    Basic idea: in situations when they do offer checking, why not give the compiler all the information you can?

    —John

      Here is an answer to your rhetorical question.

      The problem with using prototypes is that you are making assumptions for the person using your code about exactly how they are going to use it. Prototypes have many silent, nasty and unexpected side-effects. For instance perhaps the person using your code wants to have the parameters in an array. Oops, they need an array slice, but may not understand why. Perhaps they want to dynamically decide what goes into your second argument and so use a trinary operator. Oops, not allowed by the prototype, you need to use temporaries. Perhaps they are returning the output of a function to decide what your parameters should be. Oops, not allowed by the prototype, you need a temporary array and then a slice. Either that or multiple temporary scalars.

      None of these are very intuitive bugs for me to figure out. Nor when I have to bring someone up to speed on Perl do I want to waste energy on explaining the whys and wherefores of prototype-caused errors. It is simpler to just pretend that they don't exist.

      So using them, even when the usage is clear, is problematic at best. It is, in fact, very easy to achieve the same basic effect without the side-effects by a run-time check inside of the function. And the same approach will also work inside of method calls. It works no matter what order in your file the functions are declared in. Consistency and flexibility are Good. Making assumptions for your caller is Bad.

      But there are more reasons not to use prototypes for checking. When you use prototypes, you are forcing yourself into a single positional style of programming. You have put on a blinker where you are disregarding the possibilities of list-based constructs, or the use of named parameter processing. But I find that using positional logic is inherently buggy (people make mistakes with what order parameters go in), and I find tremendous flexiblity in using dynamic list-based approaches where applicable.

      So I find the win from prototypes to come with problems at best, and at worst trying to get it limits the kinds of solutions that can be tried. In other words they get me coming and going.

      All of this is why when this topic came up what I said in chatter was, Given the choice, I would refuse to work with a programmer who understood how Perl's prototypes work, understood the gotchas, and used them liberally anyways. It would be a cognitive mismatch. What I mean by that is that working with that code would be a constant source of frustration for me. And I simply would not want to do it.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (5)
As of 2024-03-19 09:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found