Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Re: What should be returned in scalar context?

by BrowserUk (Patriarch)
on Dec 02, 2003 at 19:11 UTC ( [id://311715]=note: print w/replies, xml ) Need Help??


in reply to What should be returned in scalar context?

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.

  • Comment on Re: What should be returned in scalar context?

Replies are listed 'Best First'.
Re: Re: What should be returned in scalar context?
by hardburn (Abbot) on Dec 02, 2003 at 20:24 UTC

    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!

        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.

        Sounds like a good use of junctions:

        if( want() eq all( 'SCALAR', 'STRING' ) ) { . . . }

        ----
        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

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (9)
As of 2024-04-19 09:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found