Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl using a sub call as argument to another sub - unexpected context

I am using a sub call as an argument to another sub. Example code:

test(isInString(), 'second parameter', 'third parameter');

sub test {
    my ($boolean, $second, $third) = @_;
    print "boolean: $boolean\n second: $second\n third: $third\n";
}

sub isInString {
    my $searchFor = 'a'; 
    my $searchIn = 'bcd';   

    return ($searchFor && $searchIn && ($searchIn =~ $searchFor));
}

In the example above, I would expect the return statement in "isInString" to evaluate to false ('' or undef or whatever that may be in perl), and this would be passed into "Test" as parameter #1, sou you'd essentially have

Test(undef, 'second parameter', 'third parameter');

That is not what happens though. isInString returns an empty array, and essentially what you get is

Test('second parameter', 'third parameter');

returning from isInString in the perl debugger gives:

list context return from main::isInString: empty array

I'm assuming this is a perl context thing, I can assign to a scalar variable first and it works fine:

my $bool = isInString();
Test($bool, 'second parameter', 'third parameter');

debugger gives - scalar context return from main::isInString: ''

EDIT I removed all parens with the same result:

sub isInString {
        my $searchFor = 'a'; 
        my $searchIn = 'bcd';   

        return $searchFor && $searchIn && $searchIn =~ $searchFor;
    }

Debugger still gives:

list context return from main::isInString: empty array

Can someone please explain why it is returning a list context/empty array in this scenario?

like image 525
user210757 Avatar asked Jul 30 '10 20:07

user210757


2 Answers

Your return is evaluated in list context because the function is called in list context. The boolean operators (like &&) do not force scalar context.

To quote perlop on &&:

Scalar or list context propagates down to the right operand if it is evaluated.

Since the match operator returns an empty list on failure in list context, that's what you get. If you want to force scalar context, use the scalar operator, either in your return statement, or when you call the function.

Also, the idea that parentheses force list context (as suggested by some of the other answers) is a common misconception. They don't. List context is the default; anything that could be list context is list context. That's why there's no list operator that corresponds to scalar. The one place where parens "force" list context is with the repetition operator. 'a' x 3 means 'aaa', but ('a') x 3 means ('a', 'a', 'a'). But that's not actually forcing list context, it's just a special case for the x operator.

like image 108
cjm Avatar answered Oct 13 '22 11:10

cjm


If you want to force a "boolean context" you can use the double-negative "operator" : !!, like so:

test( !!isInString(), 'second parameter', 'third parameter' );

By calling it in a list of parameters, you've forced it into a "list context". You can force it back with something like this. One bang (!) evaluates it in a boolean context and makes it either 1 or ''. But since you aren't testing for the complement, but the value itself, putting the other bang in there gets you back the value you wanted.

However, since it's pretty clear from the function body that you are returning a boolean value, and you're calling it in a list context (a list of parameters is a list context), you might want to force a defined value there. Like so:

return ($searchFor && $searchIn && $searchIn =~ $searchFor) || 0;

Any undef will result in a false value, and it will jump to the or and return the 0, which a list context will not ignore as it can an undef.

like image 38
Axeman Avatar answered Oct 13 '22 11:10

Axeman