Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Why does foo() evaluate in array context in "${\foo()}"?

by ELISHEVA (Prior)
on Apr 23, 2009 at 06:17 UTC ( #759475=perlquestion: print w/replies, xml ) Need Help??

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

Recently I discovered that foo() evaluates in array context in "${\foo()}", e.g.
use strict; use warnings; sub foo { wantarray() ? 10 : 1; } print "${\foo()}\n"; #outputs 10

I would have expected that the $ sigil would have biased "${...}" towards a scalar context since the obvious value inside of {...} would naturally be a scalar reference. Thus the thing that "\" takes a reference from should be presumed to be a scalar. Apparently this is not so.

I'm wondering if any of the Perl language design/internals gurus here would care to enlighten me? Perl generally tries to "do what is expected" (by the programmer). What was the expected understanding that behind the current behavior?

Thanks in advance, beth

Replies are listed 'Best First'.
Re: Why does foo() evaluate in array context in "${\foo()}"?
by JavaFan (Canon) on Apr 23, 2009 at 08:42 UTC
    I would have expected that the $ sigil would have biased "${...}" towards a scalar context since the obvious value inside of {...} would naturally be a scalar reference.
    You are correct in the sense that ${EXPR} provides scalar context to EXPR.
    Thus the thing that "\" takes a reference from should be presumed to be a scalar.
    But you are wrong here. You are assuming that \ propagates context. It doesn't. Only a few operators do, and \ isn't one of them. \ always provides list context to its arguments. Regardless of its own context. This is standard behaviour in Perl.
Re: Why does foo() evaluate in array context in "${\foo()}"?
by ikegami (Patriarch) on Apr 23, 2009 at 06:25 UTC

    \(LIST) is documented to return a list of references to each member of the LIST in some cases.

    >perl -e"print qq{$_: $$_\n} for \( 'a', 'b', 'c' );" SCALAR(0x239b9c): a SCALAR(0x239c9c): b SCALAR(0x239cec): c >perl -e"print qq{$_: $$_\n} for \qw( a b c );" SCALAR(0x239b9c): a SCALAR(0x239c9c): b SCALAR(0x239cec): c >perl -e"@a = qw( a b c ); print qq{$_: $$_\n} for \(@a);" SCALAR(0x239bac): a SCALAR(0x239c5c): b SCALAR(0x239cac): c >perl -e"@a = qw( a b c ); print qq{$_: @$_\n} for \@a;" ARRAY(0x1829a04): a b c

    Same goes for subs, apparently.

    >perl -e"print qq{$_: $$_\n} for \sub { qw( a b c ) }->();" SCALAR(0x239c5c): a SCALAR(0x239cac): b SCALAR(0x239cbc): c

    Update: Clarified some.

    Update: This differs from x's behaviour

    >perl -le"@a = 'a' x 5; print 0+@a" 1 >perl -le"@a = ('a') x 5; print 0+@a" 5 >perl -le"@a = qw( a ) x 5; print 0+@a" 5 >perl -le"@a = sub { qw( a ) }->() x 5; print 0+@a" 1
Re: Why does foo() evaluate in array context in "${\foo()}"?
by BrowserUk (Patriarch) on Apr 23, 2009 at 06:37 UTC

    How would you do this:

    sub foo { \('fred', 'bill') };; print "${ foo() }";; bill

    if a scalar context was implied by "${ ... }"?

    No. I can't think of a good use for it either :)

    But given you have scalar to force a scalar context, but no list to do the opposite, the status quo gives the greatest flexibility. Whether there is a good use for that flexibility it was (still is) too early to judge :)


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      This is puzzling...

      perlref says "Anywhere you’d put an identifier (or chain of identifiers) as part of a variable or subroutine name, you can replace the identifier with a BLOCK returning a reference of the correct type."

      We can see from ELISHEVA's example that the sub is called in list context. I assume, therefore, that it returns a list. But a list isn't "a reference" even if every element of the list is. Is there an undocumented case of the block returning a list? Or am I wrong to think that the sub returns a list? If it doesn't return a list, what does it return and why?

      Had the sub been called in scalar context then, because there is no such thing as a list in scalar context, what does \(LIST) become?

      sub foo { \('fred', 'bill') };; print "${ scalar(foo()) }"; __END__ Returns: bill

      So I know what happens in the end, but I'm having trouble reconciling my model of what perl does and what the various expressions mean.

      update: Considering JavaFan's post I see my mistake: the sub is called in scalar context, not list context. The call is not the same as in ELISHEVA's original post. And perlref says \($a, $b, $c) is the same as (\$a, \$b, $\c). I conclude that in scalar context the latter is simply not a list.

      update2: Changed "array context" to "list context" throughout, because that's what it is called.

      update3: Clarified what perlref says and what I conclude/assume.

        because there is no such thing as a list in scalar context,

        My take on it is that this is the exception that disproves that rule. Whilst others will disagree and probably decline to comment.

        If you put that thing--two or more things separated by commas--that we're told is not a list, in a scalar context, at compile time, then the compiler knows the list is useless and can discard all but the last element. This is shown nicely by Deparse:

        C:\test>perl -MO=Deparse -e"print scalar( 'fred', 'john', 'bill' )" print scalar('???', '???', 'bill'); -e syntax OK

        And backed up by warnings:

        C:\test>perl -we"print scalar( 'fred', 'john', 'bill' )" Useless use of a constant in void context at -e line 1. Useless use of a constant in void context at -e line 1. bill

        But construct that same thing in a way that the context is not directly discernable at compile-time, and it has no choice but to construct a list er, leave the thing intact.

        Again nicely shown by Deparse:

        C:\test>perl -MO=Deparse -e"print scalar( sub{'fred', 'john', 'bill'}- +>() )" print scalar sub { 'fred', 'john', 'bill'; } ->(); -e syntax OK

        And no warnings either:

        C:\test>perl -we"print scalar( sub{'fred', 'john', 'bill'}->() )" bill

        Just a list thing in a scalar context being reduce/resolved/resulted to the last element of that list thing.

        what does \(LIST) become?

        The only way left to describe it is that the thing (that would be a list in a list context), becomes the last element of that thing when it finds itself in a scalar context. :)


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        As JavaFan says, contrary to most expectations \ actually provides LIST context ! So:

        #!/usr/bin/perl -w use strict ; use warnings ; sub foo { my ($tag) = @_ ; print "$tag: ", wantarray ? "LIST" : "SCALAR", "\n" ; return wantarray ? ('a', 'b') : \78 ; } ; my $a = \foo('SCALAR \foo()') ; my @a = \foo('LIST \foo()') ; my $b = ${foo('SCALAR ${foo()}')} ; my @b = (${foo('LIST ${foo()}')}) ;
        gives:
        SCALAR \foo(): LIST
        LIST   \foo(): LIST
        SCALAR ${foo()}: SCALAR
        LIST   ${foo()}: SCALAR
        
        which also shows that ${...} does provide SCALAR context as expected.

        This behaviour is not mentioned in perlop and I cannot see it in perlref either :-(

        It is documented that \($a, @b, %c) is the same as (\$a, \@b, \%c). Which requires the , to be operating in LIST context -- so perhaps we should not be surprised. (It's also worth remembering that LISTs are interpolated when they are evaluated, not when they are constructed...)

Re: Why does foo() evaluate in array context in "${\foo()}"?
by repellent (Priest) on Apr 23, 2009 at 19:00 UTC
    The way I see it, ${ EXPR1 } forces scalar context and then performs a scalar dereference on EXPR1.

    The backslash \( EXPR2 ) forces list context on EXPR2 and returns a list of references.

    By combining the two, you have \foo() returning a list of one reference to scalar 10. Then, ${ (\10) } returns the scalar dereferenced 10.

    FYI, there's a catch to using the \( ) syntax for @arrays:

    Update: BTW, same catch applies to %hashes.

      there's a catch to using the \( ) syntax for @arrays:

      Wow that's weird.

      I'll agree with ikegami and say that's weird.

      But I'll also admit that I'm just saying that to make it look like I know what I am talking about. In reality the catch listed above doesn't make any sense to me.

      That is, I've read the documentation on the Perl debugger (although I've never used it myself). So I know that x @array = (1..3) evaluates the line in list context and dumps results. Listing the element indices threw me for a moment, but I blame Friday for that.

      But what, for example, would you expect for the second command? Without the backslash, I look at (@array, (@array)) and think that it's a little confusing. I think it just evaluates as the concatenation of both lists, yes? Adding the backslash and I have no idea what it should look like. What gets evaluated first? I've taken a peek at perlop but it doesn't indicate if the backslash has a higher precedence than parentheses -- I would expect no, but with the oddity here I can't say for sure.

      Any help you can give on unraveling what's going on here would be appreciated.

        The way I see it, the backslash \( ) syntax has to evaluate its arguments in list context.

        It also has to treat @arrays and %hashes with care: should it expand it out into a list & take references of its elements, or just take the reference to the actual @array / %hash itself?

        To disambiguate the two, the second command shows that perl won't expand @array / %hash into LIST unless you have specifically parenthesized it.

        The third command shows that if @array / %hash was the only argument to \( ), then perl makes the (inconsistent) choice to expand it into a LIST, and take the reference of each list element. A probable rationale is that if you wanted the reference to the actual hash itself, you would do \%hash instead of \(%hash).

        The fourth command is like the second command. The empty list puts perl back in the second command's evaluation "frame of mind".

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (3)
As of 2023-02-03 06:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    I prefer not to run the latest version of Perl because:







    Results (24 votes). Check out past polls.

    Notices?