Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Re: Context aware functions - best practices?

by sauoq (Abbot)
on Jan 15, 2003 at 06:20 UTC ( #227066=note: print w/ replies, xml ) Need Help??


in reply to Context aware functions - best practices?

Your case may be exceptional but, most often, the right thing to do is just return the array. That allows the user to get behavior he is already familiar with from dealing with plain old arrays.

sub foo { my @r = (42) x $_[0]; return @r; } my @a = foo(3); my $s = foo(3); my ($sl)= foo(3); print "\@a: @a\n"; print "\$s: $s\n"; print "\$sl: $sl\n"; my @t = (42,42,42); @a = @t; $s = @t; ($sl) = @t; print '-'x40,"\n"; print "\@a: @a\n"; print "\$s: $s\n"; print "\$sl: $sl\n"; __END__ @a: 42 42 42 $s: 3 $sl: 42 ---------------------------------------- @a: 42 42 42 $s: 3 $sl: 42

Consequently, in most cases, I think that if a user wants to call a function that is documented to return a list and assign it directly to a scalar, it is fair to ask him to remember what he is doing. By the way, I don't think the right way to do that is

my ($x) = foo(1);
but rather
my $x = (foo(1))[0];
The results may be the same but, though the latter is more verbose, it makes the desired result obvious. It doesn't look like it might have been a mistake.

All of that said, I think using wantarray is probably the right way to do it in your case. It seems unlikely that someone would want to evaluate the returned list in scalar context to get the number of elements, so you are using the facilities Perl provides to get more useful behavior out of your function.

-sauoq
"My two cents aren't worth a dime.";


Comment on Re: Context aware functions - best practices?
Select or Download Code
Re: Re: Context aware functions - best practices?
by MarkM (Curate) on Jan 15, 2003 at 07:17 UTC

    Regarding the choice to use wantarray to grant context to the return value of a subroutine, you say:

    Consequently, in most cases, I think that if a user wants to call a function that is documented to return a list and assign it directly to a scalar, it is fair to ask him to remember what he is doing.

    An argument against this is that the user expects a list to be returned, not an array. Normally when a list is used on the right side of the expression, a scalar on the left side of the expression will be given the last value in the list, not the number of items in the list. This argument at least indicates that wantarray is desirable, and that the other suggestion in this thread (returning the last item in the list) is preferable to returning the array itself.

    Further on, you suggest that:

    my $x = (foo(l))[0];

    Is preferable to:

    my ($x) = foo(1);

    In Perl, there is a clear precidence for context to be significant. Consequently, Perl programmers expect functions to return the most sensible results in either context. It is a question of programming efficiency, more than programming purity. Calling a function in scalar context is like using a contraction in the English language. "I want the simpler form."

    Often, the value in scalar context is _very_ different than than the value in list context. For example, the builtin getpwnam() functions returns the username in scalar context, while getpwuid() returns the uid. Both return the same sort of list in list context.

    In terms of intuition, most people intuitively expect the following two expressions to be equivalent:

    my($x) = foo(...);

    And:

    my $x = foo(...);

    Sometimes the above is not the case, however, the times that this is not the case are usually the exception to the rule, and not the rule itself.

    I, personally, often use "wantarray ? @array : $array[0]" as a return value, not only because I believe that this is the most intuitive behaviour, but also because in most cases, I consider this the most useful behaviour. Useful often directly equates to efficient from a programming perspective.

      An argument against this is that the user expects a list to be returned, not an array.

      While I understand your argument I believe that it is the user that errors. The error lies in his expectation. If a subroutine is documented to return a list it's also implicitly documented only for list context. When calling such a subroutine in scalar context you can't expect anything to happen--even less something sane to happen; there are no lists in scalar context. Take sort() for instance. That's obviously a list-context function, and you don't expect it to do anything useful at all in scalar context. Aristotle's problem is not as clear, but my point is still that if a subroutine is documented to return a list it's only documented for list context. Every other behaviour is undefined.

      But you also hit the nail when saying that we should code so that our routines get intuitive. So yes, I do partly agree with your argument.

      In terms of intuition, most people intuitively expect the following two expressions to be equivalent:
      my($x) = foo(...);
      And:
      my $x = foo(...);


      Yes, but this intuition is quickly counter-prooved by the numerous bugs that will come to any programmer expecting this. This is probably the first context bug that Perl programmers will meet. And usually, when past the newbie state, they don't get that wrong anymore. I too want to write easy-to-use code, but I expect a certain level of Perl understanding of my users, and list vs. scalar assignment is a basic thing in Perl.

      I'm undecided...

      ihb
        That's obviously a list-context function, and you don't expect it to do anything useful at all in scalar context.

        You make a good point here. Some functions just shouldn't be expected to do anything useful in scalar context. A nice idiom for writing such functions is to start them with wantarray or return undef.

        -sauoq
        "My two cents aren't worth a dime.";
        
      An argument against this is that the user expects a list to be returned, not an array. Normally when a list is used on the right side of the expression, a scalar on the left side of the expression will be given the last value in the list, not the number of items in the list.

      That's true when dealing with literal lists because that's how the comma operator works. Few builtin functions work this way. As far as the builtins go, there really is no standard way of relating both contexts. The perlfunc manpage says it nicely, "In general, they do what you want, unless you want consistency."

      This argument at least indicates that wantarray is desirable, and that the other suggestion in this thread (returning the last item in the list) is preferable to returning the array itself.

      There are many cases where wantarray is desirable. I even pointed out that Aristotle's case was probably one of them. I stand by my assertion, however, that most of the time returning the array makes more sense than using wantarray. For the record, I think that Zaxo's suggestion that the last element be returned is not very well thought out. For the general case, at least, it's a horrid idea. It's not exactly intuitive that a function would behave like a list literal and it wouldn't be very useful in most cases either. Besides, if you really want your function to act like a list literal, you could do it with a slice instead of wantarray; something like sub listify { @_[0..$#1] } for instance.

      In terms of intuition, most people intuitively expect the following two expressions to be equivalent:
      my($x) = foo(...);
      And:
      my $x = foo(...);
      Sometimes the above is not the case, however, the times that this is not the case are usually the exception to the rule, and not the rule itself.

      I disagree. The cases where those aren't the same are the rule. Consider

      my ($x) = split//,"foo"; # $x eq 'f' my $x = split//,"foo"; # $x == 3 my ($x) = map $_, qw(foo bar); # $x eq 'bar' my $x = map $_, qw(foo bar); # $x == 2 my ($x) = localtime; # seconds my $x = localtime; # String like ctime(3)
      The list could go on and on. So, though it's true people that are relatively new to perl might find the behavior you describe intuitive, it's also true that people with a modicum of perl experience learn not to expect it.

      I, personally, often use "wantarray ? @array : $array[0]" as a return value, not only because I believe that this is the most intuitive behaviour, but also because in most cases, I consider this the most useful behaviour. Useful often directly equates to efficient from a programming perspective.

      Again, I think this may be intuitive for someone that doesn't know perl but rather than that being the most useful behavior, I think it is simply hobbled. Here's an illustration:

      sub foo { my @a = qw(foo bar); @a } sub bar { my @a = qw(foo bar); wantarray ? @a : $a[0] } my($p) = foo; # $p eq 'foo' my $q = foo; # $q == 2 (different information) my($r) = bar; # $r eq 'foo' my $s = bar; # $s eq 'foo' (same information)
      Doing it your way, both calls result in the same information. If one wants the number of items returned, one must call it in list context first and then retrieve the count in a separate operation. Where's the efficiency in that? Allowing the call in scalar context to provide that information in exactly the way we are used to getting it from actual array variables is the most useful behavior most of the time. It makes a good default. For those times when it makes more sense to provide other information in scalar context, wantarray is available.

      -sauoq
      "My two cents aren't worth a dime.";
      

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (12)
As of 2014-07-29 09:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (212 votes), past polls