Skip to content

<<~ strips indentation — by a different rule than Perl's

Level: 101 · anyone embedding SQL, HTML or a message in code

One line: Ruby's <<~ removes the indentation of the least-indented line and ignores where the closing word sits; Perl's <<~ removes exactly the closing word's indentation and rejects a line indented less; Python's textwrap.dedent follows Ruby's rule.

Ruby added the squiggly heredoc in 2.3 and Perl added its own <<~ in 5.26. They look identical and disagree about what "the indentation" is.

Verified output of squiggly_heredoc_rb.rb — regenerated by tools/run_examples.py, never hand-typed.

<<~SQL, lines indented 6, 8 and 7, closing word indented 4:
  |SELECT name|
  |  FROM users|
  | WHERE id = 7|

<<-SQL keeps every space; only the closing word may be indented:
  |      SELECT name|

<<~'SQL' in quotes does not interpolate:
  |WHERE id = #{id}|

Reading the output

  • <<~: the three lines were indented 6, 8 and 7 spaces, so 6 came off each, and FROM and WHERE keep their relative indentation. The closing SQL was indented 4 and made no difference.
  • <<- only allows the closing word to be indented; the body keeps every space.
  • Quotes around the word turn off interpolation, as they do in a shell.

The same heredoc in Perl

Verified output of squiggly_heredoc_pl.pl — regenerated by tools/run_examples.py, never hand-typed.

<<~SQL, lines indented 6, 8 and 7, closing word indented 4:
  |  SELECT name|
  |    FROM users|
  |   WHERE id = 7|

a line indented 2 above a closing word indented 4:
  Indentation on line 1 of here-doc doesn't match delimiter

Perl took 4 spaces off every line — the closing word's indentation — so the SQL keeps two spaces it was not meant to have. And a body line indented less than the closing word is a compile error, where Ruby would simply have removed less.

Python

Verified output of squiggly_heredoc_py.py — regenerated by tools/run_examples.py, never hand-typed.

textwrap.dedent on lines indented 6, 8 and 7, last line indented 4:
  |SELECT name|
  |  FROM users|
  | WHERE id = 7|

textwrap.dedent removes what every non-blank line has in common, which is Ruby's answer.

What to do

In Ruby, indent the heredoc body to fit the code around it and put the closing word wherever it reads well. When the same text moves to Perl, line the closing word up with the least-indented line of the body.