Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

What's the better Perl style? Define parameters or not?

by pureHeart (Acolyte)
on Jan 05, 2010 at 02:55 UTC ( #815676=perlquestion: print w/replies, xml ) Need Help??

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

Hi, I'm a C/C++ programmer who is learning Perl. I'm reading several Perl books in parallel. They seem to have different preference about if function parameters are needed. So I'm seeking the opinions from the Perl experts here.

sub func($$$){ ... }
vs.
sub func{ }

The book "Perl for C Programmers" suggests you always define parameters. In page 136, it says "By using a parameter specification, you give Perl the capability to do valuable parameter checking. Leaving out the parameter specification, an you invite chaos."

In other books like "SAMs Teach Yourself Perl in 24 Hours" and "Learning Perl" (O'REILLY) never mentioned the existence of defining parameters. All examples are using "sub func{" style. E.g. I didn't know one could do something like "sub func($$$)" after reading these 2 books... until I read the book "Perl for C Programmers".

Which way is the common practice in Perl world? Thanks.
  • Comment on What's the better Perl style? Define parameters or not?

Replies are listed 'Best First'.
Re: What's the better Perl style? Define parameters or not?
by ikegami (Pope) on Jan 05, 2010 at 03:21 UTC

    "By using a parameter specification, you give Perl the capability to do valuable parameter checking."

    First of all, it's called a prototype. Prototypes alter parsing rules. To say they provide parameter checking is bit of a stretch. There are better approaches if you want parameter validation.

    The book "Perl for C Programmers" suggests you always define parameters.

    Well that's pretty silly since a sub's prototype is ignored when the sub is called as a method. Since most of my subs are methods, it's suggesting I waste my time writing and maintaining prototypes that don't get used.

    "Leaving out the parameter specification, an you invite chaos."

    If that were true, the world would have ended in chaos a long time ago from all the OOP being done in Perl.

      Well that's pretty silly since a sub's prototype is ignored when the sub is called as a method
      what!? A sub is a method right, so I assumed its called like one. Could you explain this to me ?

      cheers
      LuCa

      Update: thnx GrandFather, I never noticed this subtle difference!

        Consider:

        use strict; use warnings; my $obj = bless {}; # As function do_something (); # As method $obj->do_something (); sub do_something { my ($self) = @_; if ($self && 'main' eq ref $self) { print "Called as method\n"; } else { print "Called as function\n"; } }

        Prints:

        Called as function Called as method

        True laziness is hard work
        A sub is a method right, so I assumed its called like one.

        Prototypes change how the parser interprets the syntax of a call when Perl compiles the program. It's easy to figure out which apply() function gets called when you write:

        sub apply_arguments { my ($item, @args) = @_; for my $arg (@args) { apply( $item, @args ); } }

        It's much more difficult to figure out which apply() method gets called when you write:

        sub apply_arguments { my ($self, @args) = @_; for my $arg (@args) { $self->apply( @args ); } }

        The appropriate method may not even exist at that point in the program.

        I believe the difference is ambiguity. The procedural function is known and its signature can be examined and used. Depending on what you put in the signature, a %hash might be flattened into an @array, or converted into a \%hashref as it is shoved into @_. With a method call, you cannot be sure how a method call resolve until runtime, and by then it is too late to know what to do with the arguments.

        In Perl Best Practices, Damien recommends never using signatures. This is both because they are not available in all situations, and because they are easy to mess up. They are not worth the risk of potential problems.

        Personally, I like 'em. I don't use them for any OO work (obviously), but I do use them for procedural helper routines. This is because if I add or remove an argument and do not update it everywhere, I prefer to get the warning at compile time instead of something at runtime.

        - doug

        PS: Improved signatures is one of the perl6 things that I hope hurry up and get here.

Re: What's the better Perl style? Define parameters or not?
by Tanktalus (Canon) on Jan 05, 2010 at 04:46 UTC

    Apparently, you should throw out the Perl for C Programmers book. If that's the advice they're giving you, I have to wonder about the rest of the book.

    As ikegami said, it's called 'prototypes'. And they're evil like goto in C. If you don't know why it's evil, and what the exceptions are, don't use it. There are valid uses for it, but as a general rule, don't use it in production code. (Trying it out in play-code is fine, as that's one way to learn why it's evil, and what its uses really are. And positing questions here on said play code is fine, too.)

    These have nothing to do with prototypes in C. That is, int foo(char const* bar); ensures that the parameter passed in to foo is a pointer to constant characters (or convertible to that type, such as a char*, though the function will then treat the pointer as if it pointed to a constant). Perl doesn't have anything like that. sub foo($); says that the first parameter passed to foo is evaluated in scalar context. That's almost never what you want. And it kills a number of really useful facets of Perl that C/C++ just can't match, such as:

    sub exp($$) { # perform exponention. } my @values = ( [ 3, 3 ], # cube of 3 [ 81, 0.5 ], # square root of 81 # ... ); for my $params (@values) { printf "exp(%s,%s) = %s", @$params, exp(@$params); }
    The prototype will cause this to die. To fix it, simply remove the prototype. Or call it as "&exp(@$params)" or "exp($params->[0], $params->[1])", but those are both far uglier.

      And they're evil like goto in C.

      goto is very useful in C.

      TYPE* foo(...) { ... if (...) goto Error; ... if (...) goto Error; ... if (...) goto Error; ... return p; Error: ... cleanup acquired resources ... return NULL; }

      In Perl, not so much.

        Yes, and prototypes are the same as that. They're useful in narrow circumstances where you know that you're not violating the principles that make them evil. And you can consider the pronouns in the previous sentence to be talking about either goto in C/C++ or prototypes in Perl. Personally, I've only used prototypes when trying to duplicate map or grep-like syntax (first parameter is a code block), but, even then, I usually avoid them. I'm not sure you could get try { BLOCK } catch { BLOCK } kind of syntax without prototypes, but I wouldn't want those new to perl to be writing that code as they probably wouldn't understand the details of prototypes (or much else around the desired syntax).

Re: What's the better Perl style? Define parameters or not?
by eyepopslikeamosquito (Chancellor) on Jan 05, 2010 at 05:43 UTC

    This review of Perl for C Programmers, written by a highly respected Perl and C programmer, former Perl 5 pumpking Nicholas Clark, should prove useful in assessing the book you're currently reading.

    As for Perl books, I recommend Perl Best Practices which, in Chapter 9, page 194 recommends: "Don't use subroutine prototypes".

Re: What's the better Perl style? Define parameters or not?
by shmem (Chancellor) on Jan 05, 2010 at 09:50 UTC
Re: What's the better Perl style? Define parameters or not?
by JavaFan (Canon) on Jan 05, 2010 at 10:45 UTC
    Which way is the common practice in Perl world?
    The fast majority of code I see doesn't use prototypes, so not using prototypes is "common practice". But there's also a lot of code out there that doesn't use warnings or strict, so I wouldn't go by "common practice".

    I won't recommend what you should do. I think you make yourself familiar with the pros and cons and make a decision what to use yourself - I think that's far more useful than making a tally of the responses and just follow the majority.

    I will tell you why I don't use the $$$ prototype. Without prototypes, the following sub calls are equivalent:

    sub foo {...}; my @a = (1, 2, 3); foo(1, 2, 3); foo(@a);
    With prototypes, the second is an error:
    sub bar ($$$) {...}; my @a = (1, 2, 3); bar(1, 2, 3); bar(@a); # Not enough arguments error
    I find the non-flatting behaviour far more annoying than the benefit of the prototype checking the number of arguments.

    That isn't to say I never use prototypes. A few prototypes have their benefits, IMO. They are: the empty prototype, the $ and _ prototypes (but only if it's just $ or _, nothing else with it), and prototypes starting with one or more &, \@ or \%, optionally followed by ;@. You need those prototypes to mimic calling conventions of certain build-ins. (Functions like lc, push and grep for instance).

Re: What's the better Perl style? Define parameters or not?
by biohisham (Priest) on Jan 05, 2010 at 04:36 UTC
    That depends on the requirements and flexibility you want to impose when working with subroutines, The different ways you can declare subroutines in Perl are a combination of what you have read in these books, and generally, to brief it:
    • sub SUBNAME
    • sub SUBNAME(PROTOTYPE)
    • sub SUBNAME {BLOCK}
    • sub SUBNAME(PROTOTYPE){BLOCK}

    As the term "prototype" suggests, you can tell Perl of the type and number of arguments you wanna pass to this subroutine and whether any of these types can be optional, therefore, it is a mechanism to make sure that you don't pass an array to a subroutine when it expects a scalar...

    sub SUBNAME($@;\%)
    This subroutine above is called with a scalar, list and followed by an optional hash...

    You can call a subroutine in a number of ways too, but for simplicity I'd only mention two ways:
    &SUBNAME # & dereferencer is implicit and can be dropped at times. #OR... SUBNAME()
    How does this come in context with declaring prototypes?... using the & form of calling a subroutine would deactivate prototype checking that means, as long as you don't preface the subroutine names with & the prototypes provided in the declaration would affect/interfere with the calls to the subroutine...

    NOTE: I learnt this from The Perl Black Book...

    Update: ikegami and BrowserUk, I am happy to have erred and I am happy to have been guided, I was under the impression that prototypes could help validating a subroutine passed arguments, and just verifying this I did not get a proof of concept that such a behavior exists but this left me confused on what I read reviewing the lessons and with a question of "why use them prototypes after all if they are not good at checking functions or are ignored upon such validation?" , I really hope I am not being a dumb now...



    Excellence is an Endeavor of Persistence. Chance Favors a Prepared Mind.
      I learnt this from The Perl Black Book...

      You need to review the lessons.

      Your prototype means that any optional hashref supplied will be assigned to the array, and will never get assigned to the appropriate place inside the sub:

      sub test($@;\%){ my( $scalar, @array, $hashref ) = @_; pp $_ for $scalar, \@array, $hashref; };; $s='fred'; @a = 1..10; %h = 1..10;; test( $s, @a, \%h );; "fred" [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, { 1 => 2, 3 => 4, 5 => 6, 7 => 8, 9 => 10 }, ] undef

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

      You are also wrong about \%. \% means the sub requires a hash, not a hashref.

      >perl -le"sub f(\%) { print $_[0] } f(\%h)" Type of arg 1 to main::f must be hash (not reference constructor) at - +e line 1, at end of line Execution of -e aborted due to compilation errors. >perl -le"sub f(\%) { print $_[0] } f(%h)" HASH(0x238be4)

      A hashref is what the sub receives.

      There is no prototype that will enforce what you desire (a scalar, an array and an optional hashref). Prototypes aren't good at validation.

      sub SUBNAME($@;\%)
      This subroutine above is called with a scalar, array and followed by an optional hash...

      Others, more worthy than I, have dealt with the hashref parameter. The @ does not mean an array, it means a list. Consider:
      sub mysub($@\%) { my ($one, @two, $ref) = @_; print "@two\n"; } mysub('a','b','c','d');
      Gives:
      b c d
      The minimum length of a list is zero elements, so the following "works" (in that no error is reported) as well:
      mysub('a');
      If you want to force an array, use \@. Prototypes are useful for forcing the correct type of reference being passed at compile-time (as opposed to runtime checks) but are so easy to circumvent, even accidently, that they can lull you into a false sense of security.
Re: What's the better Perl style? Define parameters or not?
by dsheroh (Prior) on Jan 05, 2010 at 11:10 UTC
    As already noted, these "parameter specifications" are called "prototypes", but they have absolutely nothing to do with function prototypes in C. They are also generally best avoided.

    If you want something similar to C's function prototypes (parameter type/number checking, plus support for optional and/or named parameters), take a look at Devel::Declare for the general case or MooseX::Declare if you're doing Moose-based OO.

      I really appreciate all of you who provided comments. Your replies opened my eyes. Thank you.
        You may also want to look at Params::Validate and signatures if you're not doing OO programing. There are others on CPAN but these two spring to mind.


        Smoothie, smoothie, hundre prosent naturlig!

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://815676]
Approved by ikegami
Front-paged by ikegami
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (13)
As of 2019-07-19 14:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?