Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

There's scalar(), but no list(). Perl could need one for rare but reasonable use

by flowdy (Scribe)
on Jun 14, 2014 at 12:53 UTC ( [id://1089890]=perlquestion: print w/replies, xml ) Need Help??

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

Dear Monks,

Following up an earlier PM post of mine, which is Re^6: A Melancholy Monkday, and also another question in 2005, What's most efficient way to get list context?, I would still like to advocate a list() operator. I encountered edge cases where it can come in handy, would reduce hours debugging and working around the Newbies' nightmares, the dim parts in the Perl language design. I have to admit that these edge cases I pondered on have some esotheric smell, but anyway here you are:

In your opinion, is it worth a feature request?

=begin perldoc -f list

The list() operator enforces list context on its arguments. It returns always a flat list of all scalars contained in passed arrays or hashes or the values returned by subroutine calls. It enforces list context for them even where e.g. return "inherits" the scalar context from the environment of the subroutine call. It guarantees that the values are rvalues and not aliased beneath.

Example:
  • sub { return grep { ... } $your, @values, $here } returns either all grepped values or the number of them, depending on context (cf. documentation of grep). This is probably not what the users of your module want.
  • sub { return list grep { ... } $your, @values } returns in scalar context the last item of @values, so it behaves like a sub that returns a literal comma-separated list of scalars.
=end

Test code:

#!/usr/bin/perl use strict; use Test::More; # Pure perl mock-up of builtin list() counterpart to scalar() sub list { return @_[ 0 .. $#_ ]; } # Our example demo function, it doesn't do anything useful however # ---------------------------------------------------------------- sub incr_str_returned_from { my $sub_ref = shift; # You might be a careful professional and pick one explicitly: # my $scalar = ( $sub_ref->() )[-1]; # But you might be not and forget to make sure a given # sub-ref returns something useful in scalar context. You might # consider it to return a list and conclude that you'll simply # increment the last value in it. But this is not safe: my $scalar = $sub_ref->(); return ++$scalar; # (Did you know: Perl increments alphabetic # strings like numbers of base 26) } my @expected = qw( xxx foo bar baz ); is_deeply [ Client::Module::valid_scalars_1() ], \@expected, "test without list() in list context"; is_deeply [ Client::Module::valid_scalars_2() ], \@expected, "test with list() in list context"; is incr_str_returned_from(\&Client::Module::valid_scalars_1) => 5, "Oops, grep without list() maybe 'misbehaves' in scalar context:" ." gets number of elements"; is incr_str_returned_from(\&Client::Module::valid_scalars_2)=>'bba', "list(grep ...) in scalar context:" ." evaluates to last value like literal lists"; my %hash = ( a => 3 ); my @test1 = \( $hash{a}, @expected, 'zzz' ); is scalar grep( { ref eq 'SCALAR' } @test1 ), 2, "Without list(), we get a list of references to lvalues"; my @test2 = \( $hash{a}, list(@expected), 'zzz' ); is scalar grep( { ref eq 'SCALAR' } @test2 ), 6, "With list(), we get a list of references to plain rvalues"; ${ $test2[0] }++; is $hash{a}, 4, "Indirect increment via reference to hash element"; ${$test2[2]} = 'bla'; TODO: { local $TODO = "Perl core devs be welcome to decide if they " . "implement list() that way or not"; is $expected[1], 'bla', "Values returned by and passed to list() are identical"; } done_testing; package Client::Module; sub valid_scalars_1 { return grep { defined $_ } # stub grep routine 'xxx', qw(foo bar baz), undef; # demo values } sub valid_scalars_2 { return ::list valid_scalars_1() } __END__

Any comments appreciated oh wise,
-- flowdy

Replies are listed 'Best First'.
Re: There's scalar(), but no list(). Perl could need one for rare but reasonable use
by ikegami (Patriarch) on Jun 14, 2014 at 15:08 UTC

    The biggest problem with list (or rather its name) is that it perpetuates the incorrect knowledge that operators in scalar context return a list which is then coerced to return its last element. I'd rather have lastval() and count().

    Nothing stops a CPAN module from providing these. Like you said, they're rarely needed.


    By the way, the caller can grab the correct value from your example using:

    my $x = ( f(...) )[-1]; # Last value my $x = () = f(...); # Count

    In both cases, f is called in list context.

      A plain list, as I learned, is a comma-separated couple of values maybe in a pair of parens (just if operator precedence stipulates that). In scalar context, it shoves one value after the other into the single slot available, like it would distribute the values into the list context slots that will be discarded from tail to head unless saved in an array or in a couple of lvalue scalars (($foo, $bar) = ...). However actually implemented low-level, this at least is how it fits well into my overall knowledge of Perl. Mind the accordance to the fact that only what the last expression evaluated in a {...}-block returns is passed along the levels, and consider what $? would store from a system("longlist -of; external; commands") call, too.

      But it is not that easy, there are caveats, probably more than the two I outlined in the test script. I'll try to generalize the problem: If your list consists of values equal in their kind, but you put expressions in your list that have a distinct scalar context behaviour on their own, then pray they don't happen to be evaluated in scalar context as caused by higher level in the callstack (beyond your control perhaps). This would render your list of indeed not-so-equal values overtly wrong and greedy of debugging time. If there is a means to provide scalar context, another to provide list context would not only solve a seeming lack in design (just a fan of symmetry that I am), but would also make for more robust module interfaces.

      After all I've come to the conclusion that my feature request is well posted on PM. It would be ridiculous to consult p5p&co. as long as there are just two usage cases I can think of. But I'm looking forward to more cases posted by others.

        If there is a means to provide scalar context, another to provide list context would not only solve a seeming lack in design (just a fan of symmetry that I am), but would also make for more robust module interfaces.

        It's possible to return a scalar in list context (since a single scalar is zero or more scalars), which is why scalar exists.

        It's not possible to return a list in scalar context (since zero or more scalars isn't a scalar), which is why list doesn't exist.

        Adding list would not improve symmetry or robustness; it would just lie about what you are doing. If you want a function that returns the last scalar of a list or scalar, list is a shitty name.

Re: There's scalar(), but no list(). Perl could need one for rare but reasonable use
by LanX (Saint) on Jun 14, 2014 at 14:48 UTC
    Thanks for the clarification.

    The way context is propagated to the return statement (resp. the last statement) of a sub is indeed a problem.

    ( its too surprising how return 1..3 behaves and suddenly transforms into a flip flop)

    That's why subs were remodeled in Perl 6 and I'd really appreciate a similar feature or pragma switch in Perl 5.

    Regarding your list() routine, I think it solves the wrong problem . The behavior of comma separated lists comes from the "scalar comma operator" and is rarely useful.

    The IMHO "most normal behavior" is to return a count, like arrays do. This fits perfectly into boolean context! That's why I occasionally wrap my values into a @{[...]} construct.

    But that's too short to justify an own built-in function.

    Maybe something like arr() could at least add some more readability.

    YMMV! :)

    update

    To complete the picture, I was also in situations where I would have preferred $a=call() to act like:

    • ($a)=call() (great for iterators) or
    • $a=[call()] (gimme the reference)
    • simply die (never meant to return scalar)

    Tastes differ, IMHO its rather a question of clear notation.

    update

    And wantarray is not only a misnomer, but too difficult handle in 3 contexts. Damien published a module handling this which should better be XSed and Core.

    Cheers Rolf

    (addicted to the Perl Programming Language)

Re: There's scalar(), but no list(). Perl could need one for rare but reasonable use
by Anonymous Monk on Jun 14, 2014 at 15:09 UTC

    Another way (perl 5.14+):

    • sub { return splice [grep ..., @values] }

Re: There's scalar(), but no list(). Perl could need one for rare but reasonable use
by LanX (Saint) on Jun 14, 2014 at 13:09 UTC

    > Any comments appreciated oh wise,

    Answering was easier, if you didn't hide your arguments in a long block of code.

    Could you please rephrase, maybe in a list of bullet points?

    Cheers Rolf

    (addicted to the Perl Programming Language)

      I added a POD section above the code to clarify what list() essentially does.
Re: There's scalar(), but no list(). Perl could need one for rare but reasonable use
by xiaoyafeng (Deacon) on Jun 15, 2014 at 08:42 UTC

    considering its age, no wonder perl5 has many grammar in-consistence and pitfall. If you want a cleaner, more consistent language, perl6 is preferred.





    I am trying to improve my English skills, if you see a mistake please feel free to reply or /msg me a correction

      The fact that a list of scalars isn't a scalar isn't an inconsistency, and it's not a factor of its age.

      Can you return multiple values from a Perl 6 sub? Can you assign those multiple values to one variable? How does one specify which value to get from that variable?

Log In?
Username:
Password:

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

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

    No recent polls found