String interpolation
`{expression}` inside a string runs the expression and shows it, a literal brace is written `\{` or `\}`, and the expression inside the braces has to fit on one line.
A {...} inside a string literal is not text: it is an expression, run when the string is built and shown in the
result through the same Show.show every other value uses. This page is about what may stand inside the braces and
how to write a literal one.
Example#
const name = "Ada"
const remaining = 3
print "Hello, {name}. {remaining} tasks left."
print "escaped: \{not interpolated\}"Syntax#
"text {expression} text" an interpolated string
"text \{ text" a literal `{`
"text \} text" a literal `}`Rules#
{expression}is replaced byShow.showof that expression's value. Any expression that produces a value with aShowimplementation may stand inside the braces, from a name to a fulliformatch.const name = "Ada" print "verdict: {if name == "Ada" { "yes" } else { "no" }}"A literal brace is written
\{or\}. There is no other way to place a{or a}into an interpolated string; a bare{always opens an interpolation.The expression inside the braces has to fit on one line. A raw line break before the closing
}is a lexer error, because the lexer only balances braces and quotes while looking for the close, and a line break ends that search.Does not compileconst value = 1 print "total: {value }" // error: This `{` inside of a string is never closed. A literal brace is written `\{` // error: Expected an expression, found `}` // error: This string is never closedA quote or a brace inside the expression can nest, and is not mistaken for the string's own end. The lexer tracks brace depth and skips over a nested string literal as one unit while it looks for the interpolation's closing
}, which is what lets amatchor another string literal stand inside the braces.A raw string (
raw"...") never interpolates.{is an ordinary character inside one; see Literals for the two string forms.A literal where a
Regexor aUriTemplateis expected never interpolates either. It is read verbatim, so{id}is the template's variable and{2,4}the pattern's repetition; see Checked literals.A method read off a value is refused where it would be shown, with the call to write instead. Without its
(),found.startis the method bound tofound, and a function value shows as its type - so the line would print() => Int64where the answer was meant. Nobody shows a method to see its type, so the checker says the call was forgotten, inside the braces and in an argument ofprintalike. A function value in a binding, or a method named through its type (Span.start), is still shown as its type: that is written on purpose.Does not compiletype Span { low: Int fn start(): Int { low } } const found = Span(3) print "starts at {found.start}" // error: `start` is a method, and this shows the method instead of its answer
What this is not#
A literal { is not written by doubling it. Some template languages use {{ for a literal brace; here that
would open an interpolation twice, which errors as soon as the first one is never closed by a }.
print "price: \{19.99\}"print "price: {{19.99}"
// error: This `{` inside of a string is never closed. A literal brace is written `\{`Interpolation is not string concatenation with a different spelling. "{a}{b}" calls Show.show on a and on
b and places the results next to each other; it does not require either one to already be a String.
Related#
- Literals - the two string literal forms, and the escapes a non-raw one has.
- Multi-line strings - the
"""form, which interpolates the same way. - Strings - the
Stringtype itself, once interpolation has produced one.