Why does Perl evaluate code in ${...} during string interpolation?
Asked Answered
A

3

10

Why does the following snippet work at all? And what evil might be possible using this? But seriously, is there any reason, the code in ${} gets evaluated at all and then used as scalar reference?

use strict;
no strict 'refs';

our $message = "Hello world!";
print "${ lc 'MESSAGE' }\n";
Aggi answered 25/2, 2010 at 9:12 Comment(2)
You explicitly stated that you wanted to use symbolic references and perl did your bidding.Overload
That's how dereferencing works. You might find helpful: perlmonks.org/?node=References+quick+referenceOutmaneuver
A
4

It's ok, unless you use symbolic references. Suppose the following code:

my %messages = (hello => "Hello world!", bye => "Bye-bye, world!");
sub get_message_ref { return \$messages{$_[0]} }; # returns scalarref
print "${ get_message_ref('bye') }\n";

Agree, its usefulness is not obvious with scalarrefs, but it is very useful with arrayrefs.

print "keys: @{[keys %messages]}\n";
Arsenic answered 25/2, 2010 at 9:29 Comment(3)
I don't like depending on the setting of $", so I would only use that for an expression returning a single scalar. And for that, "${\expr}" is just as good as "@{[expr]}", if less symmetrical.Outmaneuver
@Outmaneuver Ok, you've caught me. I agree with you. But when you're just writing a one-time script, it's perfectly ok.Arsenic
The @ form is a lot prettier than the $ form, though.Outmaneuver
B
15

We explain this in depth in Intermediate Perl.

The general syntax for variable lookups is:

 SIGIL  BLOCK  INDEXY-THING

For a simple scalar that looks like:

 print $   { foo };

You have probably seen this when you need to separate a variable name from things surrounding it:

 print "abc${foo}def\n";

If you just have a Perl identifier in the block and no surrounding mess, you can leave off the braces, which is the common case:

 print $foo;

However, this is the same thing for dereferencing a reference:

 SIGIL  BLOCK-RETURNING-REFERENCE  INDEXY-THINGS

If the thing that you get in the block is a reference, Perl tries to dereference it like you asked it too:

 my $ref = \ '12345';
 print $     { $ref };

That's a real block though, and not just sugar. You can have as many statements as you like in there:

 print $     { my $ref = \ '1234'; $ref };

Now you're not just specifying a Perl identifier, so Perl doesn't assume that you're giving it an identifier and it executes code and uses the result as a reference. Consider the difference between these almost identical say statements:

    use 5.010;
our $foo = "I'm the scalar";

sub foo { \ "I'm the sub" }

say ${foo};
say ${foo;};

In that second say Perl sees the semi-colon, realizes it's not an identifier, interprets the code inside the braces as text, and returns the result. Since the result is a reference, it uses the ${...} to dereference it. It doesn't matter where you do this, so that you do it inside a double-quoted string is not special.

Also, notice the our there. That's important now that you're going to consider something a bit more tricky:

    use 5.010;
our $foo = "I'm the scalar";

sub foo { \ "I'm the sub" }
sub baz { 'foo' }

say ${foo};
say ${foo;};
say ${baz;};

Perl intreprets that last say as code and sees the result is not a reference; it's the simple string foo. Perl sees that it's not a reference but it's now in a dereferencing context so it does a symbolic reference (as Greg Bacon describes). Since symbolic references work with variables in the symbol table, that $foo had to be a package variable.

Since it's easy to mess this up, strict has a handy check for it. However, when you turn it off, don't be surprised when it bites you. :)

Briseno answered 25/2, 2010 at 23:9 Comment(0)
L
5

From the "Using References" section of the perlref documentation:

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. In other words, the previous examples could be written like this:

$bar = ${$scalarref};
push(@{$arrayref}, $filename);
${$arrayref}[0] = "January";
${$hashref}{"KEY"} = "VALUE";
&{$coderef}(1,2,3);
$globref->print("output\n");  # iff IO::Handle is loaded

Admittedly, it's a little silly to use the curlies in this case, but the BLOCK can contain any arbitrary expression, in particular, subscripted expressions:

&{ $dispatch{$index} }(1,2,3);    # call correct routine

Because of being able to omit the curlies for the simple case of $$x, people often make the mistake of viewing the dereferencing symbols as proper operators, and wonder about their precedence. If they were, though, you could use parentheses instead of braces. That's not the case. Consider the difference below; case 0 is a short-hand version of case 1, not case 2:

$$hashref{"KEY"}   = "VALUE";     # CASE 0
${$hashref}{"KEY"} = "VALUE";     # CASE 1
${$hashref{"KEY"}} = "VALUE";     # CASE 2
${$hashref->{"KEY"}} = "VALUE";   # CASE 3

Case 2 is also deceptive in that you're accessing a variable called %hashref, not dereferencing through $hashref to the hash it's presumably referencing. That would be case 3.

Later in "Symbolic references":

We said that references spring into existence as necessary if they are undefined, but we didn't say what happens if a value used as a reference is already defined, but isn't a hard reference. If you use it as a reference, it'll be treated as a symbolic reference. That is, the value of the scalar is taken to be the name of a variable, rather than a direct link to a (possibly) anonymous value.

Lodhia answered 25/2, 2010 at 11:39 Comment(2)
Thanks for the extensive explanation about refs, but the question was about why the contents of *{ ... } gets evaluated in a double-quoted string and if this behavior has any useful applications. :)Aggi
@Aggi Yes, and I cited specific passages in the documentation that explain what's happening: note the beginning of the first quoted sentence (emphasis added): “ANYWHERE you'd put an identifier … you can replace the identifier with a BLOCK …” A Perl block is arbitrary code surrounded by curly braces.Lodhia
A
4

It's ok, unless you use symbolic references. Suppose the following code:

my %messages = (hello => "Hello world!", bye => "Bye-bye, world!");
sub get_message_ref { return \$messages{$_[0]} }; # returns scalarref
print "${ get_message_ref('bye') }\n";

Agree, its usefulness is not obvious with scalarrefs, but it is very useful with arrayrefs.

print "keys: @{[keys %messages]}\n";
Arsenic answered 25/2, 2010 at 9:29 Comment(3)
I don't like depending on the setting of $", so I would only use that for an expression returning a single scalar. And for that, "${\expr}" is just as good as "@{[expr]}", if less symmetrical.Outmaneuver
@Outmaneuver Ok, you've caught me. I agree with you. But when you're just writing a one-time script, it's perfectly ok.Arsenic
The @ form is a lot prettier than the $ form, though.Outmaneuver

© 2022 - 2024 — McMap. All rights reserved.