<!-- category: essentials -->
Stacked Heredocs
Assign two multi-line strings in one statement:Two heredocs. Onemy ($sql, $html) = (<<'SQL', <<'HTML'); SELECT name, email FROM users WHERE active = 1 ORDER BY name SQL <div class="results"> <h2>Active Users</h2> <ul id="user-list"></ul> </div> HTML
my statement. The bodies appear one after the other, each ending with its own terminator. Perl reads them in order and assigns them left to right.
Most Perl programmers know heredocs. Some know you can use multiple heredoc markers on a single line. Almost nobody does it. Which is a shame, because for certain patterns it's exactly the right tool.
Part 1: HEREDOC RECAP
A heredoc is a multi-line string literal. The<< operator starts it, followed by a terminator tag:
Single quotes around the tag (my $text = <<'END'; This is a multi-line string that preserves all whitespace and newlines. END
<<'END') mean no interpolation. Double quotes (<<"END") or no quotes (<<END) mean variables and escape sequences are expanded:
The terminator must appear on its own line, at the start of the line, followed by nothing but a newline (or a semicolon in some cases).my $name = "World"; my $greeting = <<"GREET"; Hello, $name! Today is a good day. GREET
That's the basics. Now for the stacking.
Part 2: THE STACKING SYNTAX
Here's the key insight: the<<TAG marker is just an expression that evaluates to a string. The string body follows later, after the current statement ends. Perl collects the bodies in the order the markers appear.
So you can put multiple markers on the same line:
After the semicolon, Perl expects two heredoc bodies:my ($a, $b) = (<<"X", <<"Y"); This is the body of X. It can have multiple lines. X This is the body of Y. It also has multiple lines. Y
- First, everything up to
Xon its own line - Then, everything up to
Yon its own line
The bodies are read sequentially and assigned to $a and $b respectively.
Part 3: HOW PERL READS THEM
Let's trace exactly what happens:my ($first, $second) = (<<'A', <<'B'); alpha content A beta content B
The rule is simple: bodies appear in the same order as their markers, and each one ends with its terminator tag on a line by itself.LINE 1: my ($first, $second) = (<<'A', <<'B'); ^-- Perl sees two heredoc markers: A and B ^-- The statement ends at the semicolon ^-- Now Perl looks for the heredoc bodies LINE 2: alpha content LINE 3: A ^-- Body of first heredoc (marker A) ends here LINE 4: beta content LINE 5: B ^-- Body of second heredoc (marker B) ends here LINE 6: (next statement starts here)
You can stack more than two:
Three markers, three bodies, three assignments. Perl handles it without complaint.my ($x, $y, $z) = (<<'X', <<'Y', <<'Z'); x content X y content Y z content Z
Part 4: MIXING QUOTE STYLES
Each heredoc marker can have its own quoting style. Mix and match:The first heredoc uses single quotes, somy $name = "Perl"; my ($static, $dynamic) = (<<'LITERAL', <<"INTERPOLATED"); This is literal. No $variables are expanded here. Not even \n escape sequences. LITERAL Hello from $name! This one interpolates \t with tabs. Version: $^V INTERPOLATED
$variables appears literally. The second uses double quotes, so $name and \t are expanded.
You can even mix in a backtick heredoc for command execution:
The backtick heredoc executes its body as a shell command and captures the output. Stacked with a regular heredoc, you get a template and its data in one declaration.my ($template, $listing) = (<<'HTML', <<`CMD`); <h1>Directory Listing</h1> <pre> HTML ls -la /tmp CMD
Part 5: MULTI-PART TEMPLATES
This is where stacking shines. When you have a page template with a header and footer that wrap around dynamic content:Two related strings, declared together, used separately. The stacking makes the relationship between header and footer visually obvious. They belong together. The code says so.my ($header, $footer) = (<<'HEAD', <<'FOOT'); <!DOCTYPE html> <html> <head> <title>Report</title> <link rel="stylesheet" href="style.css"> </head> <body> HEAD </body> </html> FOOT # later... print $header; print generate_report(); print $footer;
Part 6: SQL + HTML IN ONE DECLARATION
Building a web page that shows database results? Define both the query and the wrapper template in one shot:The query and its output template live together because they're conceptually linked. If you change the SELECT columns, the template is right there, staring at you, reminding you to update it too.my ($query, $row_template) = (<<'SQL', <<'HTML'); SELECT u.name, u.email, d.name AS department FROM users u JOIN departments d ON u.dept_id = d.id WHERE u.active = 1 ORDER BY d.name, u.name SQL <tr> <td>%s</td> <td><a href="mailto:%s">%s</a></td> <td>%s</td> </tr> HTML my $sth = $dbh->prepare($query); $sth->execute; while (my @row = $sth->fetchrow_array) { printf $row_template, $row[0], $row[1], $row[1], $row[2]; }
Part 7: INDENTED HEREDOCS (<<~)
Perl 5.26 introduced<<~, the indented heredoc. It strips leading whitespace from every line based on the least-indented line. This works with stacking too:
Thesub render { my ($css, $js) = (<<~'CSS', <<~'JS'); body { font-family: monospace; background: #1a1a1a; color: #e0e0e0; } CSS document.addEventListener('DOMContentLoaded', function() { console.log('Ready'); }); JS return "<style>$css</style><script>$js</script>"; }
<<~ strips the leading whitespace so the heredoc content isn't jammed to the left margin. Your code stays indented properly inside its function. Both heredocs maintain their natural indentation without carrying it into the string value.
Note that the terminator tags (CSS and JS) must be indented to at least the same level as the content.
Part 8: READABILITY CONSIDERATIONS
Stacked heredocs are powerful. They're also a potential readability hazard if you overdo it. Some guidelines:Stack two heredocs when they're conceptually paired. Header and footer. Query and template. Request and response. Two things that belong together.
Don't stack four or five heredocs. By the time the reader reaches the third terminator, they've lost track of which body goes to which variable.# good: paired concepts my ($request_body, $expected_response) = (<<'REQ', <<'RESP'); {"action": "login", "user": "alice"} REQ {"status": "ok", "token": "abc123"} RESP
If you need more than two, use separate statements:# too much: hard to follow my ($a, $b, $c, $d) = (<<'A', <<'B', <<'C', <<'D'); ...twenty lines later you're lost...
Three separate declarations are clearer than three stacked heredocs when each one is more than a few lines long.my $header = <<'HEADER'; ... HEADER my $body = <<'BODY'; ... BODY my $footer = <<'FOOTER'; ... FOOTER
Part 9: STACKING IN FUNCTION CALLS
Heredoc markers are expressions. They work anywhere an expression works, including function arguments:The function receives three arguments: the header heredoc, whateverwrite_file('output.html', <<'HEADER', <!DOCTYPE html> <html><body> HEADER generate_content(), <<'FOOTER' </body></html> FOOTER );
generate_content() returns, and the footer heredoc. The heredoc bodies follow the closing parenthesis and semicolon.
This looks weird the first time you see it. The function call ends, then the heredoc bodies appear after the ). But it's valid Perl. The bodies are collected in order, just like always.
You can also pass stacked heredocs to print:
Two strings printed in sequence. The HTTP header and the HTML body, generated from two heredocs passed directly toprint(<<"PART1", <<"PART2"); Content-Type: text/html PART1 <html><body><p>Hello</p></body></html> PART2
print.
Part 10: WHEN TO STACK, WHEN NOT TO
Use stacked heredocs when:- Two strings are conceptually paired (header/footer, query/template)
- Both strings are short (under 10 lines each)
- Declaring them together makes the relationship clear
- You're building test fixtures (input/expected output pairs)
Use separate heredocs when:
- Each string is long
- The strings aren't conceptually related
- You need more than two
- Adding a third later would require restructuring the first statement
Use something else entirely when:
- The template is complex enough for a real template engine
- The SQL is complex enough for a query builder
- The strings are in external files
Stacked heredocs are a tool for a specific situation. They're not the default. But when the situation fits, nothing else is as clean.
perl.gg<<'A', <<'B' +---------+ | Body A | | ... | +---------+ A +---------+ | Body B | | ... | +---------+ B Two strings. One statement. Bodies in order. Simple as that. .--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/