Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

What should be returned in scalar context?

by tilly (Archbishop)
on Dec 02, 2003 at 06:42 UTC ( [id://311537]=perlmeditation: print w/replies, xml ) Need Help??

As anyone who has read and understood wantarray knows, Perl has a notion of scalar versus list context, and you can do anything that you want with that hook. The question is what the most convenient default choice is.

Perl offers you little guidance in this. For instance in list context, localtime gives you a useful list, while in scalar it gives you a formatted result. Or caller will give you an array of information versus the single most likely to be useful fact, which happens to be the first element in the list. An array slice will give you the last element.

Here is a non-exhaustive set of choices that I have personally made in real code:

# In these examples, @ret is your return in list context. return @ret; return wantarray ? @ret : \@ret; return @ret[0..$#ret]; return wantarray ? @ret : $ret[0]; if (wantarray) { return @ret; } elsif (1 == @ret) { return $ret[0]; } else { croak("<function> did not produce a scalar return"); } # And for something completely different... sub foo { if (1 != @_) { return map foo($_), @_; } # body of foo here. return $whatever; }
As you can see, I am hardly consistent. I also haven't thought deeply about this particular decision. (Truth to tell, my personal opinion is that the entire idea of context in Perl is an interesting experiment in language design that other languages have wisely decided not to borrow...) However it is an important choice, and I've been feeling that I shouldn't shortchange it as much as I have so far.

Therefore I am interested in which of the above (or other variations of your choice) people think is a good default behaviour to standardize on..and more importantly why.

Thoughts?

UPDATE: Removed typo in last example caught by converter. (I had a $ in front of foo.)

Replies are listed 'Best First'.
Re: What should be returned in scalar context?
by Abigail-II (Bishop) on Dec 02, 2003 at 10:00 UTC
    As you can see, I am hardly consistent.
    So? Perl isn't consistent in this aspect either, and I think, rightly so. What to do in scalar vs list context is something you should decide on a case by case basis, IMO. So, my answer to
    Therefore I am interested in which of the above (or other variations of your choice) people think is a good default behaviour to standardize on..and more importantly why.
    is that I think standardizing is the wrong thing to do.
    # And for something completely different... sub foo { if (1 != @_) { return map $foo($_), @_; } # body of foo here. return $whatever; }
    Why the test on the number of arguments, and the recursion? Wouldn't the following be equivalent?:
    sub foo { map { # body of foo here. $whatever } @_ }

    Abigail

      Answering the last question first, no that isn't equivalent to just have a map since a map in scalar context coerces like an array does - it tells you how many elements that you have, not what any of them are.

      I have used that style in functions which transform elements where you might reasonably want to transform one or many elements. The hidden assumption is that it only makes sense to impose scalar context when you are transforming a single element, so behaving badly if you try to transform multiple elements in scalar context is OK.

      As for why I would like to standardize somewhat, like I said before, it is all about setting expectations. The trouble with doing something different in each case is underscored by the fact that a top-notch Perl programmer like yourself could be tripped up by what a built-in function does in scalar context. (And to be honest upon seeing you claim that the two should be the same, I actually ran a test program before I was confident in claiming that map had the behaviour that I was specifically trying to work around.)

      Besides which, I think that it is overkill to have to give an issue like this serious consideration with every function that I write. Having a default that just flows from my fingers would smooth out the development process.

        Besides which, I think that it is overkill to have to give an issue like this serious consideration with every function that I write. Having a default that just flows from my fingers would smooth out the development process.
        It's not that for every function I write I contemplate what I am going to do in list context, and what I am going to do in scalar context. I just do whatever seems right at the time, and that's usually the right choice. But it's seldom consistent.

        Abigail

        Tilly:
        # And for something completely different... sub foo { if (1 != @_) { return map $foo($_), @_; } # body of foo here. return $whatever; }
        Abigail-II:
        Why the test on the number of arguments, and the recursion? Wouldn't the following be equivalent?:
        sub foo { map { # body of foo here. $whatever } @_ }
        Tilly:
        Answering the last question first, no that isn't equivalent to just have a map since a map in scalar context coerces like an array does - it tells you how many elements that you have, not what any of them are.
        The original function also has a map in scalar context problem. Something like this is probably what you want:
        sub foo { (map { # body of foo here. $whatever } @_)[0..$#_]; }
        Though I this may be clearer:
        sub foo { my @ret = map { # body of foo here $whatever; } @_; @ret[0..$#ret]; }
Re: What should be returned in scalar context?
by Juerd (Abbot) on Dec 02, 2003 at 08:03 UTC

    When the function is localtime-ish, I make it work like that. I have several that return a human readable string in scalar context, but a list of (numeric) elements in list context.

    If the function returns a list that has a logical order and a variable number of elements, I return wantarray ? @array : \@array. But not when the function has a prototype of (&@).

    I find return @array; annoying, unless the function warns or dies in scalar context. Again, an exception is made for map-ish functions.

    I often use void context for mutating values: (Or: The function becomes a procedure in void context)

    my @bar = encode @foo; # mutated copy in @bar my $bar = encode @foo; # mutated copy in @$bar encode @foo; # @foo's elements are now encoded
    This is great when working with a hash of things that are about to be displayed, transmitted, etc:
    my $row = $db->query('select ...')->hash; encode values %$row;

    Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

      I guess that I have been indoctrinated enough into the concept that side-effects in functions are a bad thing that I strongly avoid writing a function that causes side-effects. (Particularly if it only does it in one context.)

      As for return @array; - that made sense to me when I was writing a function that I needed in list context and didn't want to think through scalar context. So I left it with the easiest behaviour to implement, knowing full well that it wouldn't do anything useful in scalar context, meaning that if I ever called it in scalar context then I would have motivation to fix it with some supposition about what it should do in scalar context.

        I guess that I have been indoctrinated enough into the concept that side-effects in functions are a bad thing that I strongly avoid writing a function that causes side-effects. (Particularly if it only does it in one context.)

        Side effects are okay, as long as they're documented clearly. The efficiency that you get by mutating values instead without copying them first is worth it, in my opinion. I usually document the functions exactly as in my post: a piece of code that calls the function/procedure in all three contexts, each with a useful comment.

        As for return @array; - (...) So I left it with the easiest behaviour to implement, knowing full well that it wouldn't do anything useful in scalar context, (...).

        Of course, there isn't always a good scalar value. Hence the "unless the function warns or dies in scalar context." in my post. I tend to carp +(caller 0)[3] . ' used in non-list context' unless wantarray;.

        Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

Re: What should be returned in scalar context?
by jeffa (Bishop) on Dec 02, 2003 at 13:13 UTC
    How about returning an iterator in scalar context? For example, XML::XPath (and a lot of the XML::* modules for that matter) has a method findnodes() that returns such:
    if ($results->isa('XML::XPath::NodeSet')) { return wantarray ? $results->get_nodelist : $results; # return $results->get_nodelist; }
    The docs for Tie::Array::Iterable need some corrections, and you don't actually do the tieing ... but here is an example with it:
    #!/usr/bin/perl -l use strict; use warnings; use Tie::Array::Iterable; sub foo { my @list = 0..9; return wantarray ? @list : Tie::Array::Iterable->new(@list); } # access as a list print for foo(); # or an iterator my $iter = foo()->from_start(); print ($iter->value),$iter->next until $iter->at_end;

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      That isn't an option that I would have thought of. I'll need to think through how it fits in my general programming style.
      But that make you do different things in scalar and list context from the callers point of view. Here are two examples where the caller does the same in scalar and list context. First one returns an iterator that is context aware, in the second example the function itself is a context aware iterator.
      #!/usr/bin/perl use strict; use warnings; sub make_list { my @list = @_; my $count = 0; sub {wantarray ? do {$count = 0; @list} : $list [$count ++]} } my $list = make_list qw /red green blue white brown purple/; print scalar $list -> (), "\n"; print scalar $list -> (), "\n"; print join " " => $list -> (), "\n"; my @list = qw /one two three four five six/; my $count = 0; sub foo { wantarray ? do {$count = 0; @list} : $list [$count ++] } print scalar foo, "\n"; print scalar foo, "\n"; print join " " => foo, "\n"; __END__ red green red green blue white brown purple one two one two three four five six

      Abigail

Re: What should be returned in scalar context?
by jweed (Chaplain) on Dec 02, 2003 at 07:38 UTC

    To me, it depends A LOT on what the function is being used for. The inconsistency within built in perl functions demonstrates this sort of dwimmery in action.

    I tend to prefer the second option, though (return wantarray ? @ret : \@ret;) because I don't like the idea of discarding any data when scalar context is forced. The reference still behaves nicely in boolean context, but it contains all the information of the original array."



    Who is Kayser Söze?
      I used the second option for a while because I ran across some advocacy from someone who thought that it was nice to give people who wanted the option of a slight performance increase a chance to get it.

      I have since then drifted away from using it, and the experience of extending the test suite for Text::xSV (which used that interface) has convinced me that it is a complete PITA to work with unless you only want one scalar value.

      (Yeah, yeah. I know how to use appropriately placed parens to force list context. To me tricks like that exemplify what people dislike about Perl.)

      The reference still behaves nicely in boolean context, but it contains all the information of the original array.
      What if the array is empty, how is the reference useful then in boolean context (or do you return undef in that case)?
Re: What should be returned in scalar context?
by simonm (Vicar) on Dec 02, 2003 at 08:15 UTC
    An interesting side-note: I've recently played around a bit with testing for null context (undefined wantarray), and using it to skip calculation of a return value. It's not a widely-used idiom, but I like it...

      null context

      It's called void context, not null context.

      Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

Re: What should be returned in scalar context?
by dragonchild (Archbishop) on Dec 02, 2003 at 13:55 UTC
    I'm with Abigail-II here. I don't think it's useful to standardize on a behavior when dwimmery is involved. Perl's own functions are not standardized, and I wouldn't want to be held to a standard behavior when my particular function doesn't work that way.

    I do think that many of the internals should check for void context, if possible, like what Juerd said (and like what map does). For example, uc (and friends) would be great like that. I hate having to write $x = uc $x; when the dwimmiest way would be uc $x; and have it mutate in void context.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      I hate having to write $x = uc $x; when the dwimmiest way would be uc $x; and have it mutate in void context.

      The funny (?) thing is that Perl itself is inconsistent when it comes to operators that change strings.
      MutatingNot mutating
      chomp
      chop
      s///
      tr///
      lc
      lcfirst
      quotemeta
      uc
      ucfirst

      Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

        As of last week, Larry's plan is to make Perl 6 much more consistent in that respect. My speculation (not his official pronouncement) is that there'll be two versions of each operator, one that works in-place and one that returns the modified value, leaving the original untouched.

        It'll probably go further than Ruby's convention of postfixing ! to methods that operate in-place, perhaps having language support for adding this behavior to your own operators.

        It's only in the idea stages, though.

Re: What should be returned in scalar context?
by demerphq (Chancellor) on Dec 02, 2003 at 17:51 UTC

    Truth to tell, my personal opinion is that the entire idea of context in Perl is an interesting experiment in language design that other languages have wisely decided not to borrow...

    Im on the other side on this one for sure. Context is one my favorite things in perl. Sure occassionally I get betting by it one way or another, but overall I think its a great thing.

    Therefore I am interested in which of the above (or other variations of your choice) people think is a good default behaviour to standardize on..and more importantly why.

    Im definately in the don't standardize camp. I would say that part of the artistry of perl is choosing things like this. Its like naming functions. To borrow a chess metaphor, a newbie perl programmer doesnt think about names or context at all. An experienced one deliberates on such things for ages before deciding on the right behaviour, and a grandmaster just picks a name and behaviour and it makes sense. The whole point here is convenience. Does the code read well in its various usages, is the behaviour intuitive and easy to assimilate. Or does it read like gobbleygook and trip you up all the time?

    Only experience, and understanding of the usage context (using context at the human level and not perl level) will determine what behaviour is sensible.

    I will say however that the whole

    return wantarray ? @array : \@ref

    idea doesnt seem like a clean solution. I used to do it a fair bit but I found it trips you up too often. Instead I almost always simply return the reference for things like this. In other scenarios I often make the results vary based on wantarrays status. For instance ive done stuff like this on many an occassion:

    sub get_rec { my $self=shift; print(join ",",@$self),return unless defined wantarray; return wantarray ? @$self : join(",",@$self); }

    Anyway, i would say that context is a bit like a rich cake. A little bit goes a long way, and too much is just awful. But none is even worse. :-)


    ---
    demerphq

      First they ignore you, then they laugh at you, then they fight you, then you win.
      -- Gandhi


      We are definitely in different camps when it comes to context. I prefer having my choices be more meaningful.

      Context has never bought me much that I care about. I cannot count the number of times that I have seen people tripped up by it. And, having done it, I never again want to go through the fun of wrapping code whose behaviour is heavily context-dependent. Adding a million and one precisely defined contexts to Perl 6 will move that from being a PITA to effectively impossible.

      Arguments about the artistry of Perl I find strangely unmoving. The theory that someone, somewhere, gets it right by habit is all fine and dandy. I daresay that I have more Perl expertise than most, and I sure as heck know that I don't get it right. When I revisit old code, it is a constant complaint. When I visit other people's code, their choices tend to be an irritation for me.

      YMMV and apparently does. But I'm someone who is bugged by the feeling of solving artificially imposed problems, and I've come to feel that context is one of those. :-(

      (Of course Perl avoids a lot of artificial problems that I see in other languages. I can live with the occasional misfeature that I dislike...)

        Well, my view on context is that its a wonderul thing that is easily inappropriately used. I'd guess that I use it in less than 10% of the subroutines I write, but when it makes sense I dont hesitate at all. Used sparingly, only when it makes semantic sense, I feel it reduces the chance for errors.

        As for artistry, I think I may not have been quite as clear as I wanted to. I didnt mean to imply the aristry of Perl, but rather artistry of programming. Designing an intuitive, flexible powerful, extensible interface to a body of code IMO requires an artistic touch. Without the art the interface quickly becomes annoying, frustrating, and counterintuitive. An example for instance is accessors in Perl. Do you do them as lvalue subs (I probably wouldn't), seperate gets and sets ala Java, VB etc? (nope blech), single get/set accessors that return the old value (occasionally but not often) or ones that return $self on set? (my preference.) These are artistic decisions. Theres no science involved. They are all functionally equivelent, but from an aesthetic side (look and feel) they are very different. How many times have you used a crappy API and thought to yourself "this API is soooo clunky", im betting lots just like me. I would argue that the clunky API is the product of an unartistic programmer, and a little bit of art would have improved matters greatly.

        Anyway, from the artistic point of view you might consider wantarray to be like neon lime green/yellow. Not a lot of paintings would benefit from the color, but its not like you are going to just throw away the tube of paint: one day itll be just the right shade...

        YMMV and apparently does. But I'm someone who is bugged by the feeling of solving artificially imposed problems, and I've come to feel that context is one of those. :-(

        To me context is like the invisible argument. If it were to be removed then we would probably end up requiring a flag in the parameters, or worse duplicate subroutines, to do the same thing. I think that this would be worse than the occasional mishap that occurs due to context. So to me its not an artificially imposed problem, its an artifically imposed solution. ;-)

        This reminds me of a comment I saw once. Some languages totally dispose of pointers (perl references). VB is an example. The argument goes that pointers are a consistent source of error so remove them entirely. The problem then becomes that for nontrivial work you need pointers. So what do you end up doing? Reinventing them. In VB this means using variant arrays and all the horrors that entails. The comment was: "Dont remove features just because they are a source of error, doing so will only require the user to hand code an equivelent, and this is likely to be even worse than the original problem." I would say this applies nicely to context. Removing it would only result in worse problems. At least with context the rules are relatively simple, but more importantly uniform for all subroutines. (I mean rules of how context works, not what a routine returns in a given context.)

        A rule of thumb for context IMO is that if the effect of context doesnt make sense when the code is read aloud then it shouldnt be provided, but if it would make sense, then it should be.

        Anyway, as always tilly, a thought provoking thread.


        ---
        demerphq

          First they ignore you, then they laugh at you, then they fight you, then you win.
          -- Gandhi


Re: What should be returned in scalar context? (best practice)
by sauoq (Abbot) on Dec 03, 2003 at 00:15 UTC

    Expectations should be defined by the language, not the programmer. Therefore, I think we should standardize on... well, nothing. Because Perl doesn't. I do, however, believe there is a best practice: unless you have a very good reason, it is best to sidestep the issue entirely and let Perl provide behavior that is already familiar to the programmer.

    It is easy to imagine a function where it makes the most sense to return a reference to an array in scalar context. Likewise, it is easy to imagine one where it makes the most sense to return a formatted string. Neither would make sense as a default though. They are special cases.

    In fact, the only time you should be mucking around with the return value in different contexts is when you have a special case. (And it just so happens that many, if not most, of Perl's builtins are sensibly treated as special cases.)

    The right "default" behavior, in my opinion, is to return @ret; and let the programmer using your function sort it out. Document that your function returns an array and be done with it. Perl does a fine job of defining expectations for arrays, and your function can rely on those...

    my $nelts = @array; # Number of elements. my ($first) = @array; # First element. my @whole = @array; # Whole thing. sub some_func { return @array } my $nelts = some_func(); # Number of elements. my ($first) = some_func(); # First element. my @whole = some_func(); # Whole thing.
    That's about as consistent as it gets in Perl. It'll serve you well too, should you need to globally replace an array with a function call or vice-versa.

    I think that returning a list literal (such as you create with the slice @ret[0 .. $#ret] in your example) is sorta-semi-okay as an alternative, but probably not nearly as useful. It also isn't very common practice and should probably be much more carefully documented when used. Still, I suppose Perl provides the right expectations insofar as $x = qw( a b c ); and sub f { qw(a b c) }; $x = f(); both set $x equal to 'c'.

    Out of all of your examples, the one that makes me cringe is your "something completely different" example. Why go to the trouble ensuring my $i = foo(); is equivalent to my ($i) = foo(); when most perl programmers have already gone to the trouble to learn that they probably aren't? It hardly seems worth the obfuscation. I expect most, if they ran across that function, would spend much longer trying to figure out exactly why you did it that way than they ever would working out a bug caused by calling it in scalar context. I include myself in that.

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

      The right "default" behavior, in my opinion, is to return @ret; and let the programmer using your function sort it out. Document that your function returns an array and be done with it.

      No, please don't!

      Document that it returns a list. It's not possible to return an array. It is possible to return an array reference, but this code is not doing that. If you document that some functions returns an array, the user can only guess: is it a list or an array reference?

      Document that your function returns a list and be done with it.

      Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

        But if you say return @ret you aren't returning a list when called in scalar context. You have to say return @ret[0..$#ret] for that.
        Document that it returns a list. It's not possible to return an array.

        No. I would (will and do) document that it returns an array. What you are saying might be true from an internals standpoint, but that's irrelevant. Read the code: return @ret; . . . it says "return" and is followed by an array. More importantly, it behaves like an array. It doesn't behave like a list literal (a slice does, however, as ysth points out.) It certainly doesn't behave like an array reference. Documenting that it returns an array is the only clear way to describe its behavior.

        If you document that some functions returns an array, the user can only guess: is it a list or an array reference?

        When a funtion returns an array reference, document that it returns an array reference. "Array" and "array reference" are not synonyms. The user doesn't have to guess! He just has to read.

        If you sometimes describe a reference to an array as "an array", then you are being sloppy and you should stop.

        -sauoq
        "My two cents aren't worth a dime.";
        
          A reply falls below the community's threshold of quality. You may see it by logging in.
      Yes. Yes! If there is a "right" answer to tilly's question, it seems to me like this is it -- given that he was asking what should be the default behavior.

      I ++'d a lot of nodes on this thread, but I wish I could "+=3" on this one by sauoq.

Re: What should be returned in scalar context?
by samtregar (Abbot) on Dec 02, 2003 at 18:29 UTC
    I think that use of wantarray() to modify return behavior should be generally avoided. In my opinion it is a clear violation of the principal of least surprise.

    As an example, consider this real-world case from Bricolage:

    @stories = Bric::Biz::Asset::Business::Story->list(); if (not @stories) { print "There are no stories.\n"; }

    That works fine, but this doesn't:

    if (not Bric::Biz::Asset::Business::Story->list()) { print "There are no stories.\n"; }

    That's because Bricolage list() methods (and lots of other Bricolage methods) try to be helpful and return array refs in scalar context. I've personally found a number of bugs that turned out to be caused by this problem, and I'm sure there are more waiting to be found.

    There may be cases where wantarray() is useful, but I definitely don't consider it a general-purpose tool.

    -sam

      I think that use of wantarray() to modify return behavior should be generally avoided. In my opinion it is a clear violation of the principal of least surprise.
      This obviously depends on what exactly is being returned in the first pace, but in general I agree. When you don't have any particular reason to think about context, don't use wantarray. I find that, often, the 'problem' solves itself without me thinking about it.

      Having said that, I do use wantarry sometimes, and my reasons are usually effiency related:

      • Runtime efficiency

        Returning an arrayref instead of a list, thus avoiding copying many scalars. Well, we can't agree on everything :-)
      • Programmer efficiency

        Using count(*) instead of select( values ... ) in an SQL query, if you only want to know the number of rows matching. This saves the user from having to remember yet another subroutine/method name, and me from having to put the WHERE clause in 2 places (or factoring it out).

        Also, it seems like a better idea than just returning the last column, or the number of columns, or anything like that, which would likely be the result of not using wantarray in this case.

      There might be better examples, but I'm too lazy to think of them now.

      Joost.

      -- #!/usr/bin/perl -w use strict;$;= ";Jtunsitr pa;ngo;t1h\$e;r. )p.e(r;ls ;h;a;c.k^e;rs ";$_=$;;do{$..=chop}while(chop);$_=$;;eval$.;
Re: What should be returned in scalar context?
by hardburn (Abbot) on Dec 02, 2003 at 15:03 UTC

    One thing I've done in one project using Class::DBI is to return a closure-as-an-iterator in scalar context (and void context, just because I'm lazy):

    use My::DBI::Table; # A Class::DBI instance my $table; # Intitilized elsewhere sub get_foos { my $foos = $table->foos(); if(wantarray) { my @array; while(my $foo = $foos->next()) { push @array, $foo; } return @array; } else { return sub { my $foo = $foos->next() or return; return $foo; }; } }

    In the real project, the objects returned are actually passed into the constructor of a class that acts as a middle layer between the UI and the database, so the real method is slightly more complex than the above. As is, the above is just a thin layer over the regular Class::DBI methods (which already returns iterators on has_many relationships).

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    : () { :|:& };:

    Note: All code is untested, unless otherwise stated

Sub::Context (Re: ... returned in scalar context)
by Ovid (Cardinal) on Dec 02, 2003 at 17:12 UTC

    One thing which I have been meaning to do, but as usual never quite found enough tuits for, was write Sub::Context. With this module, subroutines with context attributes would be expected to return an array or list, but in scalar context would automagically behave as the context attribute specifies without the programmer being forced to write the code for every case.

    use Sub::Context qw/arrayref first iterator custom/; sub foo : arrayref { # return wantarray ? @results : \@results; } sub bar : first { # return wantarray ? @results : $results[0]; } sub baz : iterator { # return wantarray ? @results : get_iterator(@results); } sub quux : custom { # user-defined return behavior? }

    I've had the sticky note for this module on my desk for about three months now. This seems like as good a place as any for asking for suggestions on the interface and behavior.

    Update: chromatic just reminded me that not only is there a module named Sub::Context, which he wrote (and which I've seen, darn it), it does what I was looking for, but in a somewhat different fashion.

    Cheers,
    Ovid

    New address of my CGI Course.

      This sounds useful to me. OTOH, I think you're missing some. :last and :count you should have, if only for completeness. :uselessvoid should give warnings if called in void context. :mutatevoid would set $_[0] to the return if used in void context. :warnscalar would warn if called in scalar context. :loop would do the close equivlent of tilly's if @_>1: when called in list context, with more then one arg, just run through the sub for each argument.

      Upon reading the update: Oh. I have to say, I like your API better then chromatic's, but see how both could be good in different circumstances. Perhaps you should find another namespace for yours?

      Update: Fixed minor markup issue, thanks ysth.


      Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

Re: What should be returned in scalar context? (frog)
by tye (Sage) on Dec 03, 2003 at 07:14 UTC

    The only default I can think of that makes much sense to me is:

    croak ... if ! wantarray;

    So, if you haven't taken the time to decide what should be returned by your list-returning function when used in a scalar context, it makes some sense to prevent people from using your function that way (yet), perhaps even telling the user that you haven't decided what that is supposed to mean yet.

    After you've used the function for a while, a sane choice may well become rather obvious and you can put the new scalar-context behavior in place, quite confident that you aren't breaking any existing code.

                    - tye
Re: What should be returned in scalar context?
by duff (Parson) on Dec 02, 2003 at 15:56 UTC

    I don't think there should be a "standard". That's why this particular feature is in the hands of the programmer; so that they can make the decision about what's best. Sometimes you want to return the first element of an array in scalar context, sometimes you want to return something else.

    Why do you think that there should be some standard?

    Also, you have completely ignored void context. wantarray returns 3 things you know. Do you think that there should be some "standard" for what to do in void context too?

      I am not talking a standard that is imposed on the programmer.

      I am saying that I as a programmer need to make this choice fairly often, what choices should I tend to make, and why?

      As for what to do in void context, my default has always been to treat it like scalar, but some people have already said that they standardizing on having functions mutate the input in place in void context. So yes, thinking through void context as well might make sense. (Though I probably won't because the only suggestion that I have seen runs too far counter to the rest of my personal style.)

        I am saying that I as a programmer need to make this choice fairly often, what choices should I tend to make, and why?

        You use wantarray that much? I've used it less than a couple of handfuls of times in the 11+ years I've been using perl. Most of the time I write subroutines that return lists, I say that's what they do. Period. It's rare that I need different functionality depending on context. It's only when I think "it sure would be nice if this routine did this sometimes and that other times" that wantarray might come into the picture. And when that happens, I've already decided what I need from scalar, list and void contexts. (I don't usually care about void context though)

Re: What should be returned in scalar context?
by BrowserUk (Patriarch) on Dec 02, 2003 at 19:11 UTC

    The simplest answer to the question is 'it depends'. However, there are a couple of things that I started doing fairly early on in my use of perl. The first is that in most cases, subs that wishes to utilise the calling context to differeciate what they return, should probably be testing for context as the (one of) the first things they do, rather than leaving it as something done on the return line. If the natural scalar context of a sub that can return a list is to return only the size of the list, then there is some economy in knowing that up front. It's inherently cheaper to increment a count for return than to accumulate an array and then discard it and only return the size. Equally, if a sub is called in a void context it is better to to return immediatly having done nothing rather than do stuff and then throw it away. If the sub has side effects and only returns a status value, then if called in a void context, it can be nice to have a (configurable) option to die if the sub fails -- I wish that this was an option for many of the built-in subs.

    I don't view the use of context as being unique to perl, although the way it is manifest in other languages is somewhat different. In C++ for example, it could be argued that the incorporation of a methods return type into the method signature is a form of context. The difference is that it is handled entirely by the compiler rather than by the programmer.

    One arguement for this approach is that it removes one detail from the auspices of the programmer and is therefore a good thing.

    However, an alternative view is that this then requires the programmer to code multiple methods, one for each possible context, and/or requires the use of explicit casting.

    Far from being a failed experiment, I think that context is a potentially very useful feature of perl that is only let down by it's currently limited form. IMO there are three limitatations to the current mechanism.

    1. The number of detectable contexts is too limited with just void, scalar and list.

      I would like to see this extended by

      • The subdivision of scalar context into string and numeric. And the numeric context subdivided into integer and real.
      • List context should be subdivided into list, array and hash context.
      • The addition of a "reference" context.
      • The addition of an "alias" context.
    2. The inconsistancy with which the current contexts are interpreted by implementers and implementations, not least by perl itself.
    3. The limitations and inconsistancies of the perl prototyping mechanism.

    From what I've seen of P6, all three of these are being addressed in ways that hopefully will make the whole concept of contexts become more useful and consistant. Who knows, maybe they will prove to be so useful that other languages might copy the idea in the future.

      The subdivision of scalar context into string and numeric. And the numeric context subdivided into integer and real.

      That might be easy in these cases:

      if( $foo == bar($baz) ) { . . . } if( $foo eq bar($baz) ) { . . . }

      But how could you handle the simple case of assigning the return value to a variable? I suppose the current type of the SV could be used if it already had data in it, but what about variables that were just declared?

      List context should be subdivided into list, array and hash context.

      I know the differences between list and array context are very subtle, but I'm not sure that there is any use for distinguishing the two here.

      ----
      I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
      -- Schemer

      : () { :|:& };:

      Note: All code is untested, unless otherwise stated

        But how could you handle the simple case of assigning the return value to a variable?

        The keyword was "subdivision". When the context cannot be broken down to a string or numeric context as in the case of assignment to a variable, then the context would be returned as simply 'SCALAR'. If the context can be further broken down, then that context would also be indicated. I haven't thought through how this would be done, but one way might be to return 'SCALAR/STRING' and 'SCALAR/NUMERIC' respectively in the case of your two if statements. Instead of coding if( want() eq 'SCALAR' ) {

        You would code if( want() =~ /^SCALAR/ ) { if you were only interested in determining scalar context. Integer and real contexts could be a supplement to that.

        Alternatively, use a parameter to want() if( want( 'SCALAR' ) ) { would return true if the context was scalar string, scalar numeric, scalar numeric real or scalar numeric integer, but if( want( 'INTEGER' ) ) { would only return true if the return was being used in an inherently integer context like an array indices, range boundary or as an argument to a function that had been indicated as an integer (using the much improved prototyping facility:).

        The main reason I thought of for wanting to distinguish between a list context and an array context, is the following

        sub dostuff { my @array; # doing stuff building @array return @array; } ... my @returned = dostuff();

        In the above scenario, we have a lexically local @array built up within the sub. When it comes time to return this to the caller, the array is flattened to a list, which currently appears to consume some extra memory over the array itself. On the basis of experiment, this appears to be less than the full size of the array, but is still a fairly substantial chunk of memory if the array is large. This list is then assigned to the array in the outer code consuming another large chunk of ram.

        Crude measurements indicate that the total memory allocated by the simple act of returning an array through a list to another array is close to 3 times the original size of the array. For small arrays, not a problem, but for large ones this is expensive.

        Yes, you can return a reference to the array, but the extra level of indirection involved can be a PITA, especially if the elements of the array are themselves compound structures.

        My thinking is that if the programmer could determine that the destination is an array, then he might also be given access to an alias to that destination array and assign his return values directly to it.

        sub DoSomething { my @array; alias( @array ) if want( 'ARRAY' ); #Do Stuff Directly to the destination array return; } ... my @returned = DoSomething();

        This would effectively use the same mechanism as for loops do now.

        If the results of the sub were being used in a list context, being pushed onto an array or printed in the calling code and the algorithm of the sub permits, then it can sometimes make sense not to acumulate the data into an array internally, but rather simple build and return a list.

        sub DoStuff { die 'Nothing to do in a void context' if want( 'VOID' ); if( want( 'LIST' ) ) { return map{ # Do stuff to generate the list } ...; } my @array; alias( @array ) if want( 'ARRAY' ); push @array, $stuff for ....; return \@array if want( 'ARRAY/REF' ); # The callers array has been filled directly # If we were called in an array context. return; }

        LW and TheDamian are probably way ahead of me on this, and it is possible that some of the benefits of this type of optimisation can be achieved transparently by comterpreter, but I think that there are cases were it would be useful for the programmer to have this level of control.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        Hooray!
        Wanted!

Re: What should be returned in scalar context?
by Aristotle (Chancellor) on Dec 03, 2003 at 07:08 UTC

    I strongly dislike return wantarray ? @ret : \@ret;. The justification that it offers performance benefits for those who want them reeks of premature optimization; if it turns out to be necessary, I'll have the function always return a reference. If it's not necessary, having the function set up this way anyway makes it inconvenient to deal with cases where the result list is expected to have only a single element. It also leads to a conundrum when the array is empty; some people will want to be able to dereference the return value without checking for undef, while others will want the scalar context return value to sensibly work as a boolean. It just doesn't work out; I would never consider this style in any case.

    I tend to return @ret[ 0 .. $#ret ]; these days and find it to be completely unsurprising most of the time. It is hardly a rule though, and I may do other things on other occasions.

    When we're talking about methods rather than plain functions, and the returned list consists of objects, an iterator object is a helpful option. A particularly nice kind of iterator object relays certain method calls to all of its contained list's objects, so that you can do something like $family->children->sleep(); and have all the children collectively leave for bed.


    sub foo { if (1 != @_) { return map foo($_), @_; } # body of foo here. return $whatever; }
    This seems to indicate a 1:1 mapping. In that case I offer
    sub foo { ( map { # body of foo here. } @_ )[ 0 .. $#_ ]; }

    Makeshifts last the longest.

Re: What should be returned in scalar context?
by ihb (Deacon) on Dec 03, 2003 at 03:54 UTC

    I usually just document my functions to be defined in list context only if that's all I've considered. I someone wants to use it in scalar context it's their dare.

    Personally I haven't devoloped a default idea for what you ask of, since I haven't had a need to. For instance, if someone just wants the first return value they may do ($foo) = foo(). If someone wants to get a specific element from it they may do (foo())[$n]. If they want it as a reference, they're free to construct one on the fly: [ foo() ]. If [ foo() ] would prove inefficient I'd usually prefer providing another function for getting a reference, e.g. foo_ref(), and not add behaviour to the existing routine.

    There is one special case though, and that's for methods that have one-to-one input-to-output list length mapping. If two arguments are taken, two elements are returned. It may then look like

    my ($foo, $bar) = burk('foo', 'bar'); my ($foo, $bar, $baz) = burk('foo', 'bar', 'baz');

    and in this particular case I can find it DWIMy to let

    my ($foo) = burk('foo');

    be equivalent to

    my $foo = burk('foo');

    but

    my $foo = burk('foo', 'bar');

    would emit a warning, since one can guess that this was unintended.

    In real life it could look like this:

    my $foo = foo(burk());

    and &burk is believed to return just one element. Perhaps the user was wrong in that, and &burk returned more than one element. Or perhaps &burk needed to be called in scalar context to give the expected return behaviour of one element, like localtime(). (Note that this thusly just isn't a short-cut but also imposes extra checks on &burks return.)

    To sum it up as a general rule of thumb: If a subroutine has the character of returning a list, but sometimes returns just one element, then it can be made so that in scalar context it returns that very element. But if it's called in scalar context and it in list context would have returned more than one element then it should warn.

    Code-wise this means

    return @results if wantarray; carp("More than one value in result in scalar context for &foo") if @results > 1; return $results[0];

    This whole post was pretty much a condensed revised repost of the relevant things in posts Re: Context aware functions - best practices? and Re: Re: Re: Context aware functions - best practices?. (Both nodes can be worth reading plus demerphq's reply, dispite (or because) their tendency to elaborate and digress.)

    Just my thoughts,
    ihb
Re: What should be returned in scalar context?
by vacant (Pilgrim) on Dec 03, 2003 at 05:33 UTC
    I guess I would have to ask just how often is this really an issue? Once I got over the shock of having the language automatically adjust to the context, it looked like merely a convenience to me. In other words, when I write a function, I usually know whether I want the result as a scalar or an array.

    Of course, if the function is for "public" consumption, the return type should be specified in the documentation as well as in the comments preceeding the function.

    So far as "standardizing" goes, I think I agree with those who don't standardize. So long as the function is documented properly, why not write in whatever manner best fits its purpose?

Log In?
Username:
Password:

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

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

    No recent polls found