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

Are prototypes evil?

by Django (Pilgrim)
on Sep 02, 2002 at 20:56 UTC ( #194637=perlmeditation: print w/replies, xml ) Need Help??

Dear Monks,
these thoughts arose in another thread, so I quote for introduction:
Django "Why do you consider Perl 5 prototypes as evil? I like the ability to use subs like built-ins."
tadman "They are poison. You should only use them when you have to, like forcing an array or subroutine reference..." <whole reply>

I don't think that prototypes (sub($){}, not software prototypes), or any other of Perls possibilities can be condemned in general. (well, you may call map wicked ;) As long as you know what you are doing, and why you are doing it that way, you can benefit from every technique IMHO. But I'm just another Perl newbie, and would appreciate to learn from your experience. So, is code like the following only useful for poetry? Is it a triumph of lazyness? Is it dangerous? Any suggestions are welcome.

sub Learn ($); sub expand ($); sub smile (); Learn $Perl, expand $wisdom && smile;

update: added semicolons to the declarations, Zaxo pointed the error out.

"Why don't we ever challenge the spherical earth theory?"

Replies are listed 'Best First'.
Re: Are prototypes evil?
by Aristotle (Chancellor) on Sep 02, 2002 at 22:19 UTC
    Prototypes are one of those things that you shouldn't use, unless you understand why you shouldn't use them. Consider
    sub foo ($$) { print join " / ", map "'$_'", @_; } my @x = qw(a b); foo(@x);
    This will print '2'. Not 'a' / 'b' as one might expect. Prototypes in Perl aren't. They would probably best be called "context coercion templates". A $ will coerce anything into a scalar. List/hash flattening no longer applies. When using a single ($), it's not too bad, but if you use prototypes for more parameters, you can easily surprise users of that function (including yourself). The only "prototype" I'd seriously consider is &, which lets me pass a bare(!) BLOCK that implicitly becomes a subref to a closure to the function.

    Makeshifts last the longest.

Re: Are prototypes evil?
by Zaxo (Archbishop) on Sep 02, 2002 at 23:59 UTC
    [They're] not bad, they're just drawn that way.

    A significant number of perl wizards I respect seem to detest them. I don't, but they have pretty limited use. Usually, perl's loose argument handling is just what you want, and prototypes only get in the way.

    It is a source of confusion to Javians and C-ites alike that perl's prototypes are static but not strict regarding type. And, maybe most of all, optional. Perl prototypes confound the expectations of both groups.

    There are two particular uses for prototypes which I recommend. An empty prototype, '()', says that the compiler should look for invariant return values and, if found, treat the sub as a constant at compile time. That is the magic behind

    The other is in overriding or overloading core functions. Certain core functions like push, pop, splice, chomp, substr, to name a few, modify one of their arguments in-place. That means the argument must be an lvalue. Prototypes can tell the compiler to pass by reference to an lvalue based on position in the arg list.

    (Added) The theme is that perl prototypes are compiler directives much like pragmata and attributes. They are not scaffolding for any kind of object system, consistency check, or polymorphism. /Added

    Your example needs a code block after each prototype.

    After Compline,

Re: Are prototypes evil?
by greenFox (Vicar) on Sep 03, 2002 at 02:16 UTC
    Have you seen Tom Christiansen's Prototypes in Perl? I haven't seen the article refuted so I assume it is still the last word on prototypes...

    Until you've lost your reputation, you never realize what a burden it was or what freedom really is. -Margaret Mitchell

Re: Are prototypes evil?
by demerphq (Chancellor) on Sep 03, 2002 at 11:19 UTC
    Prototypes are like guns and nuclear power. Not necessarily evil per se, but so inclined to abuse that to consider them not evil would be to underestimate a very dangerous thing.

    Many people _try_ to make prototypes do things that they are _not_ meant to be used for. Such as argument checking. A classic example would be a programmer who writes a function that takes three arguments. So they stick a ($$$) prototype on the sub thinking that now their sub will be compile time checked for recieving three scalars as arguments. Well, it will be checked that it recieves three objects, but it doesnt check that they are scalars, rather it silently coerces whatever it receives into scalars. This means it doesnt behave the way things normally should behave. Furthermore if for some reason i have the parameters I want to pass in an array, I can't just use the array and expect it to be listified into the arguments as normal, rather perl will coerce it into a scalar (the arrays count) and then complain about the missing arguments. All of this is contrary to the behaviour of normal subroutines. While this contraryness will be figured out (immediatley to eventually depending on the perl skill levels involved) it will take time and frustration.

    Basically perl is full of exceptions to rules, and exceptions to exceptions to the rules. In many ways this makes perl all the more powerful and interesting. However when completely unusual exceptions to the rules are stuck into code, with apparently no good reason (most times there are better ways to do what people think they are doing with prototypes) it just makes everyones life all the more difficult.

    sub foo ($$$) { return "A: $_[0] B: $_[1] C: $_[2]\n"; } my @arg1=("Foo"); my @arg2=("Bar"); my @arg3=("Baz"); print foo(@arg1,@arg2,@arg3); # Ack whats this? # A: 1 B: 1 C: 1 __END__ push @arg1,@arg2,@arg3; print foo(@arg1); # whatdoyoumeanthisdoesntwork!
    About the only time that I can see prototypes making sense is when the behaviour of a sub should mimic the behaviour of a perl builtin, or when you want to make a "map-like sub":
    sub funky(&$) { my $sub=shift; my $string=shift; $sub->() foreach split //,$string; } funky {print $_} "yabbadabbadoo";
    Also note that prototypes dont work with method calls, nor do they apply in a number of special cases (exceptions that while I know they exist, I cant remember the details of).

    Incidentally I wrote a node about all of this some time back: When to use Prototypes? (the answer: almost never)

    Yves / DeMerphq
    Software Engineering is Programming when you can't. -- E. W. Dijkstra (RIP)

Re: Are prototypes evil?
by theorbtwo (Prior) on Sep 03, 2002 at 01:15 UTC

    BTW, to learn about what perl6 parameter lists (using the term "prototype" is to become a flameworthy offense), look at RFC 128. (Of course, there's no guarantee that Larry will do it that way -- but it is by TheDamian.)

    (Update: I can't spell, thanks Aristotle.)

    Confession: It does an Immortal Body good.

Re: Are prototypes evil?
by tadman (Prior) on Sep 03, 2002 at 07:11 UTC
    When I see prototypes, I hear a little voice in my head, like I was James Woods in Videodrome: "Kill them. Kill them all. Kill your prototypes." I even have a script that strips them out. The number of occasions where I've had to use a prototype is less than a few dozen, and even then, it was mostly bad Laziness. Most of the ones I see are pointless, such as prototypes on object methods.

    The whole automatic scalar conversion thing really burns me. Or at least cramps my style. They don't even work properly, either, so at best, they're broken, at worst, dangerous.

    Now as for lvalue subroutines...
      Hear hear!

      Can I have a copy of the script you mention?

      Yves / DeMerphq
      Software Engineering is Programming when you can't. -- E. W. Dijkstra (RIP)

        It's just a simple snippet, really. Not 100% effective, but good enough for what I needed.
Re: Are prototypes evil?
by atcroft (Abbot) on Sep 02, 2002 at 21:30 UTC

    I have only started to work with prototypes somewhat myself, but my basic opinion (unless someone instructs me otherwise) is that they are not evil. While I can't recall for sure if they cause the engine to check to make sure the number and types of parameters passed are good, they do at least provide a convenient way to document the types and order of values to be passed, which at least would seem to me to help when someone is looking at a module written using them.

    I look forward to the comments of others, though, on how they apply them, how effective they have been as a tool for others, or reasons to avoid them.

      they do at least provide a convenient way to document the types and order of values to be passed, which at least would seem to me to help when someone is looking at a module written using them.

      Please no, No, NO, NO, NO! Say it ain't so! Comments are a convenient way to document the types and order of subroutine parameters; prototypes most certainly are not. That is arguably true in just about any language. In Perl, it is doubly so because using prototypes in places where they aren't needed creates an enormous maintenance hassle.

      I strongly encourage you to read Tom Christiansen's Prototypes in Perl which greenFox also mentioned in his reply above. You might be surprised to see how much trouble "prototypes" can get you into.

      "My two cents aren't worth a dime.";
Re: Are prototypes evil?
by lestrrat (Deacon) on Sep 03, 2002 at 10:05 UTC

    I think when they are called for ( such as when you described "... I like the ability to use subs like built-ins.." ), they can be good. The reason most people that I know detest them is because it gives you false sense of security

    For example, you may think that a sub foo($) would force perl to raise an error if you passed an array to it. But this produces no errors:

    my @list = qw/ foo bar baz /; foo( @list );

    instead, you get size of the list.

    I find this to be extremely dangerous, because a lot of people expect "prototypes" to work sort of like C/C++/Java prototypes, and what's worse, it sometimes seems to work that way as well (For example, foo($a,$b) would in fact generate an error)

    That's exactly why I don't like them, and almost never use them except for in esoteric cases. At the very least, I think the use should NOT be encouraged.

Re: Are prototypes evil?
by BrowserUk (Patriarch) on Sep 02, 2002 at 21:40 UTC

    I guess the best answer is the very last part of the "Prototypes" section of perlman:perlsub which concludes with:

    And the split gets called in scalar context so it starts scribbling on your @_ parameter list. Ouch!

    Effectively, using prototypes throws away one of the nicest and most unique features of perl, namely the context sensitivity of the calling eviroment. Using them renders wantarray superfluous.

    Well It's better than the Abottoire, but Yorkshire!

      I think you're mixing up two things:
      A ($) prototype would force scalar input to a sub, but that doesn't have any influence on the return context.
      Look at this:

      sub Input ($) { wantarray ? split /,/ : $_; } my @List = Input $Source;

      "Why don't we ever challenge the spherical earth theory?"

        Your right! I was talking twaddle!

Re: Are prototypes evil?
by talexb (Chancellor) on Sep 03, 2002 at 12:50 UTC
    Wow. I had heard of prototypes in Perl (and think they are indispensable in C programming), but hadn't read up on them. ++ to Aristotle for his clear explanation (as well as others). Was there an RFC for the prototype feature in Perl that explains why it was implemented differently than C's prototypes?

    In case there are non-C programmers out there, let me explain about C prototypes and why they're fantastic. For example, the string compare function strcmp has the following prototype:

      int strcmp ( const char *s1, const char *s2 );
    This means that you gotta call strcmp with two char * variables, and the variable you store the result in has gotta be an int. Use anything else, and the compiler will issue a warning.

    The difference already explained in numerous replies is that instead of complaining that there is a mis-match between the prototype and the call, Perl just 'fixes' the call. I wonder if there should be a use that enables a warning when Perl has 'fixed' something. Then again, that might only happen at run-time. Interesting question.

    --t. alex
    but my friends call me T.

      If you read the FMTYEWTK article (there are links in this thread) by Tom C. then youll get a good idea of what prototypes are for, but heres a quickie explanation

      Basically prototypes are a workaround for specifying input templates (they would be better called "function parameter context coercion templates" or something like it.) The idea is that perls tokenizer/lexer needs to handle a variety of ways that a function can be called, and which context its parameters should be in. For example take the scalar function. It puts its argument a scalar context, but it also needs to know that it accepts only a single item (ie have its argument _in_ a scalar context too). Otherwise the below code would print out "6" and not "5-Bar".

      sub foo{ print join "-",@_}; my @array=qw(a b c d e); foo(scalar @array,"Bar");
      Think about it, if we wrote scalar() by hand we might code it something like:
      sub my_scalar { my $count=0; $count++ foreach @_; return $count; }
      But of course then we couldnt say foo(my_scalar @array,"bar") as it would return the wrong value. We would have to write it as foo(my_scalar(@array),"bar") which im sure you admit would be a PIA considering the number of times and places you would have to do so. This kind of thing gets even more serious when you consider subs like push() which expect the @array as the first value. Without prototypes we would have to do push(\@somearray,@values) otherwise the array would become listified byt the subroutine call.

      Templates also perform a limited kind of type validation, but, and heres the problem, not consistantly. And not in the way they think. For instance

      sub listy(\@){ print join("*",@{$_[0]}),"\n"; } listy @array;
      Ensures that the first object is indeed an array, but then silently converts it to a reference to the array and passes it on. It will of course perform a certain level of type validation as it will barf on a non array being in that spot, but this behaviour is inconsistent as a '$' prototype will _not_ complain when a non scalar is provided, but rather simply "scalarify" whatever is provided. (Whichever form of scalarification makes the most sense.)

      So prototypes are a kludgy work around to some problems in parsing the fairly loose rules of perl, not a way to get your parameters checked at run time.

      Do take the time to read Tom C's article. I have only made a minor attempt at explaining the issues here.

      Incidentally there are some rare situations where prototypes come in useful. Examples may be found in some of Michael Schwern's modules for instance in Carp::Assert you will find

      sub assert ($;$) { unless($_[0]) { require Carp; Carp::confess( _fail_msg($_[1]) ); } return undef; }
      So why did he use prototypes here? Well he wanted people to be able to say
      assert @array,"Array must have elements within";
      instead of
      assert scalar @array,"...";
      This is what prototypes in perl are for, making the semantics of a subroutine perfom "naturally" in context. Unfortunately when people try to use them as parameter validation mechanisms the resulting subs tend to behave "unnaturally", as they stop behaving as functions would be expected to behave. And this is IMO evil.


      Yves / DeMerphq
      Software Engineering is Programming when you can't. -- E. W. Dijkstra (RIP)

      Then again, that might only happen at run-time.

      That's not a "might", it's bullseye.

      The compiler cannot know what's going to happen. You might think it will; if you see foo(@bar) and the prototype is ($), it's obviously a violation, right? But what happens when I have foo(bar())? In fact, the function might return a single return value some of the time and multiple ones at other times. There's no way for the compiler to know. It can only be determined at runtime. Prototypes as they're known from statically, strongly typed languages are not possible in Perl due to the very nature of the language.

      Makeshifts last the longest.

Re: Are prototypes evil?
by MrNobo1024 (Hermit) on Sep 03, 2002 at 16:57 UTC
    IMHO, prototypes are a good thing. People sometimes complain about things like sprintf(@fmt_and_args) not working, but it is very rare to have both the format and arguments in the same array.

    Now, let's see a sub where prototypes are very helpful:

    sub dmap (&\@@) { my $cl = shift; my $list1 = shift; my $list2 = \@_; my @results; for my $i(0..$#$list1) { local $a = $list1->[$i]; local $b = $list2->[$i]; push @results, &$cl; } return @results; }
    So, to do a hyper-add on two lists with that, "dmap { $a + $b } @foo, @bar". Without the prototype, that would have to have been written as the uglier "dmap sub { $a + $b }, \@foo, @bar"

    Update: Changed $#list1 to $#$list1. Thanks sauoq.


      So, to do a hyper-add on two lists with that, "dmap { $a + $b } @foo, @bar". Without the prototype, that would have to have been written as the uglier "dmap sub { $a + $b }, \@foo, @bar"

      If you wanted to use a literal list or the output of another sub as the second argument to dmap() as it is written in your node, you'd have to resort to dereferencing anonymous arrayrefs such as: @{[ your_sub_or_list_here ]} and that's just as ugly if not more so. The other choice would be to circumvent the "prototype" by calling it as &dmap( ... ) instead.

      It might be "uglier" for some calls, but by writing it without the prototype, you encourage consistency which translates to cleaner code overall.

      "My two cents aren't worth a dime.";
Re: Are prototypes evil?
by sauoq (Abbot) on Sep 03, 2002 at 20:16 UTC

    Yes, they are evil, but they wouldn't be if they had simply been called something other than "prototypes."

    "My two cents aren't worth a dime.";
      Interesting concept. What's in a name? Just good vs. evil.

Log In?

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

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (4)
As of 2023-10-01 03:01 GMT
Find Nodes?
    Voting Booth?

    No recent polls found