perl.gg / hidden-gems

String Interpolation

2024-06-29

SUBROUTINE INTERPOLATION IN PERL STRINGS

Ever tried to call a subroutine inside a double-quoted string and wondered why it didn't work? You're not alone. Perl's string interpolation is powerful, but it needs a little coaxing when it comes to subroutines.

Let's unlock this hidden gem together.

Part 1: THE PROBLEM

You might think this would work:
say "Hello, get_name()!";
But nope - that just prints the literal text "get_name()".

Perl interpolates variables like $name and @items, but it doesn't automatically recognize bare subroutine calls. We need to wrap them in a special syntax.

Part 2: SCALAR CONTEXT WITH \${\()}

When you want to interpolate a subroutine that returns a single value (scalar context), use the ${\()} construct:
sub get_greeting { return "Welcome to Perl"; } say "Hello! ${\(get_greeting())}"; # Output: Hello! Welcome to Perl
What's happening here? Let's break it down:
${ ... } - Dereference a scalar reference \( ... ) - Create a reference to the result get_greeting() - Your subroutine call
The backslash creates a reference, and ${ } dereferences it right back. It's a round trip that tricks Perl into evaluating your subroutine inside the string.

Part 3: LIST CONTEXT WITH @{[]}

When your subroutine returns a list, or you want to preserve list context, use @{[]}:
sub get_top_cities { return ("Tokyo", "Paris", "New York"); } say "Cities: @{[get_top_cities()]}"; # Output: Cities: Tokyo Paris New York
Want them comma-separated? Combine with join:
say "Cities: @{[join(', ', get_top_cities())]}"; # Output: Cities: Tokyo, Paris, New York
The @{[]} construct works similarly:
@{ ... } - Dereference an array reference [ ... ] - Create an anonymous array reference
The square brackets capture the list into an arrayref, then @{ } expands it back into the string with spaces between elements (or whatever $" is set to).

Part 4: ANONYMOUS SUBROUTINES

You can even define and call anonymous subs inline:
my $multiplier = 5; say "Result: ${\(sub { return 6 * $multiplier }->())}"; # Output: Result: 30
Or with list context:
say "Doubled: @{[map { $_ * 2 } (1, 2, 3)]}"; # Output: Doubled: 2 4 6
This is particularly handy for quick transformations without defining a named subroutine.

Part 5: PRACTICAL EXAMPLES

Timestamps in log messages:
sub timestamp { my @t = localtime; return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]); } say "[${\(timestamp())}] Application started";
Dynamic pluralization:
sub pluralize { my ($count, $word) = @_; return $count == 1 ? "$count $word" : "$count ${word}s"; } my $files = 7; say "Processing ${\(pluralize($files, 'file'))}..."; # Output: Processing 7 files...
Conditional content:
my $user = "Alice"; my $is_admin = 1; say "Welcome, $user${\($is_admin ? ' (Administrator)' : '')}!"; # Output: Welcome, Alice (Administrator)!

Part 6: MEMORY AID

Here's a simple way to remember which to use:
${\()} - Dollar sign for Scalar (single value) @{[]} - At sign for Array (list of values)
Both use the same principle: create a reference, then immediately dereference it inside the string.

CONCLUSION

String interpolation with subroutines might look a bit cryptic at first, but once you understand the reference/dereference dance, it becomes second nature.

${\(scalar_sub())} - for single values @{[list_sub()]} - for lists
These constructs let you build dynamic strings without awkward concatenation or temporary variables. Clean, expressive, and very Perlish.

perl.gg