Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much

Function Prototype Context Conversion

by tadman (Prior)
on Jun 12, 2002 at 19:52 UTC ( #173979=perlquestion: print w/replies, xml ) Need Help??

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

Quick quiz: What is the output from the following program?
#!/usr/bin/perl -w use strict; sub foo($) { print "$_[0]\n"; } my $foo = 'foo'; my @foo = ($foo); foo($foo); foo(@foo); foo($foo[0]);
If you figured there was no difference, you're in for a surprise. With a regular subroutine, this is what happens, but prototype declaration makes things quite different.

Although it makes sense in a strange kind of way, why can't @foo be treated as an implicit list for DWIM purposes?

Replies are listed 'Best First'.
Re: Function Prototype Context Conversion
by ariels (Curate) on Jun 12, 2002 at 20:05 UTC
    ...why can't @foo be treated as an implicit list for DWIM purposes?
    Because you asked Perl to treat it specially!

    Think of it like this: when you say sub foo($), you're telling Perl to convert to a scalar the first arg of foo. When you say ``foo(@foo)'', you know that @foo has only one element (and therefore can assume you know what you mean), but perl doesn't. All perl looks at is the syntax of your code: an array is being used in a scalar context. So it gets converted to a scalar. Your "rule" for converting only works for 1-element lists; Perl's rule for converting an array to a scalar works for any size array.

    It looks like another behaviour of Perl's conversion semantics that manages to confuse people.

Re: Function Prototype Context Conversion
by japhy (Canon) on Jun 12, 2002 at 20:05 UTC
    Perhaps you don't know that what the $ prototype does is place scalar(...) around the given argument to the function.
    sub foo ($$) { print "<@_>\n"; } foo(@this, %that); # foo(scalar(@this), scalar(%that));
    An array evaluated in scalar context returns its size.

    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

Re: Function Prototype Context Conversion
by Juerd (Abbot) on Jun 12, 2002 at 20:16 UTC
Re: Function Prototype Context Conversion
by chromatic (Archbishop) on Jun 12, 2002 at 19:57 UTC
    Why can't @foo be treated as an implicit list for DWIM purposes?

    Because that would make the following code terribly ambiguous:

    my @foo = ( 1 .. 5 ); my $list-to-scalar = @foo; my $length-of-array = @foo;

      No, that much is clearly understood. What I mean is that with respect to function prototypes, passing an array to a scalar prototype would seem to be a type mismatch, although these are kind of relative in such a loosely typed language as Perl. You'd hope for a warning, perhaps. You've asked for a scalar, but you're getting a list.

      What I was expecting was that the @foo array would be expanded into the brackets in the function call and passed through as a list, but Perl appears to be doing some truly clever things to try and handle the prototype, taking it to the ultimate extreme.

      japhy and everyone else who has been kind enough to post remarks on this subject is 100% correct, of course. What led to this insight into prototypes was one part ass, one part you, one part me, and we all know what that adds up to.

      The counter-intuitive component of this is that I'd expected an automatic array-to-list conversion to be peformed before said list is then passed to the prototype, but this is not the case. A list of an array is not the same as a list of the array elements.
        tadman: You've asked for a scalar, but you're getting a list.

        Ah, not so! You've ask for a scalar, but you're getting an array! If you had done:

        sub foo ($) { ... } @a = (1,10,100); foo(@a); # no error, '3' is passed to foo() foo(1,10,100); # error, too many args
        you'd have seen. Arrays and lists are not the same thing.

        Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
        s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

Re: Function Prototype Context Conversion
by Abigail-II (Bishop) on Jun 13, 2002 at 11:00 UTC
    why can't @foo be treated as an implicit list for DWIM purposes?

    What DWIM purposes? You want a scalar as first argument to foo, so Perl is doing exactly that, evaluating the first argument of foo in scalar context.

    This is a known issue with prototypes, and one of the reasons prototypes in Perl are often thought of a "a failed experiment". If you prototype foo as taking one scalar as argument, and you want foo @foo to succeed, what's the point of using a scalar prototype? Any argument is going to be scalar anyway. The only "use" could be to fail if @foo != 1. But that means you cannot do prototype checking at compile time, but have to defer it to runtime. But the neat thing about prototypes is compile time checking.


      Well, it is clear the way Perl operates, but that is merely one interpretation of how it could be done. What burned me was that I do this sort of thing all the time with regular, unprototyped functions. It just goes to show you never can be too careful when programming.

      I suppose the unfortunate thing is that Perl doesn't differentiate between "scalars" and scalar-like things in the same way that it differentiates between "arrays" and lists. Using prototypes to merely mandate how many paramters should be passed to a function doesn't seem to be the best way to go about it anyway.

      Maybe I'm too shell oriented, where I expect things to be automatically expanded before being processed. Meaning, something like this:
      % /bin/foo foo_a foo_b foo_c
      Where there you have three distinct parameters being passed to /bin/foo and it will complain if it doesn't have, let's say, at least two.
      % /bin/foo foo_?
      Now here there's technically one element being passed to the shell, which expands it into a list before being proccesed. Same thing goes if you had a variable $FOO which was 'foo_a foo_b foo_c' of course.

      In summary, I suppose the only way to do this reliably is something like this:
      sub foo { croak "foo() only takes one argument" if (@_ > 1); print "$_[0]\n"; }
      But you're not likely to see that put into general practice any time soon.

      I understand that Perl and the shell are two different things, so this whole thing is really just one big assumption that I made.

      The only reason I would even think of Perl functions and command-line programs as the same thing is the whole laissez-faire way of handling arguments that Perl has. They're not typed (normally), they're not named, heck, they're not even declared unless you use prototypes! In that regard, I was figuring that @_ would operate along the same lines as @ARGV since the two are so alike in that respect.
        It all has to do with compile time and run time. Prototyping has a compile time effect, while the number of elements in an array isn't known till run time. I fail the see the relevancy of your shell example. Prototyping changes the way perl parses Perl. Expecting no changes when using prototypes is a mistake (if you don't want changes, don't change your code! ;-)).


Log In?

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

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (7)
As of 2020-05-27 09:51 GMT
Find Nodes?
    Voting Booth?
    If programming languages were movie genres, Perl would be:

    Results (154 votes). Check out past polls.