Dragon

F-strings and the Format Mini-Language

Building a string out of values is the single most common thing text code does, and Dragon's answer is the f-string - the same one Python 3.6 introduced, and the form you should reach for by default. Prefix a string with f and any {expression} inside it is evaluated and interpolated in place. There is no +-concatenation ceremony, no str() wrapping, no positional % placeholders to keep in sync.

const w: str = "world"
print(f"hello {w}")        # hello world
print(f"sum is {2 + 3}")   # sum is 5

Anything that's an expression can go in the braces - arithmetic, a method call, an attribute access:

const name: str = "Dragon"
print(f"{name.upper()} has {len(name)} letters")   # DRAGON has 6 letters

The quote rule

A {...} slot can hold a string literal, but it has to use the other quote style, or it will close the f-string early. Inside a double-quoted f-string, use single-quoted dict keys - or dot-access, which sidesteps the issue entirely:

const ages: dict[str, int] = {"Ada": 36}
print(f"{ages['Ada']}")   # 36   - single-quoted key inside a double-quoted f-string
print(f"{ages.Ada}")      # 36   - or dot-access for identifier keys

The format mini-language

After a : inside the braces comes a format specifier that controls width, alignment, sign, base, grouping, and precision. Dragon implements Python's full mini-language. The grammar is:

{value:[[fill]align][sign][#][0][width][grouping][.precision][type]}

Every piece is optional; you use only the parts you need.

Width and alignment

A bare number sets the minimum field width. Numbers right-align by default, strings left-align. <, >, and ^ force left, right, and center; a character before the align symbol is the fill:

const x: int = 42
print(f"[{x:<6}]")    # [42    ]
print(f"[{x:>6}]")    # [    42]
print(f"[{x:^6}]")    # [  42  ]
print(f"[{x:*^6}]")   # [**42**]   - '*' fill, centered

const s: str = "hi"
print(f"[{s:>10}]")   # [        hi]
print(f"[{s:^10}]")   # [    hi    ]

Sign

+ shows a sign on positive numbers too; a space reserves a column for it:

const pi: float = 3.14159
print(f"{pi:+.2f}")   # +3.14

Precision and float types

.N sets the number of fractional digits for f, or significant digits for g. The type letter picks the presentation: f fixed-point, e/E scientific, g/G the shortest of the two, % percentage (scaled by 100):

const pi: float = 3.14159
print(f"{pi:.2f}")     # 3.14
print(f"{pi:10.2f}")   # "      3.14"   - width 10, right-aligned
print(f"{pi:010.2f}")  # 0000003.14     - zero-padded
print(f"{pi:.3g}")     # 3.14

const r: float = 0.25
print(f"{r:.1%}")      # 25.0%

Integer types and grouping

For integers, the type letter sets the base - d decimal, b binary, o octal, x/X hex - and # adds the 0x/0o/0b prefix. , and _ group digits for readability:

const n: int = 255
print(f"{n:05d}")    # 00255   - zero-pad to width 5
print(f"{n:08b}")    # 11111111
print(f"{n:x}")      # ff
print(f"{n:#x}")     # 0xff
print(f"{n:#o}")     # 0o377

const big: int = 1234567
print(f"{big:,}")    # 1,234,567
print(f"{big:_d}")   # 1_234_567

Strings in the mini-language

A string value accepts fill, alignment, and width - the padding shown above - but not numeric pieces like precision or sign, which have no meaning for text.

At a glance

SpecEffectExample → output
{x}interpolatef"{2+3}"5
{x:.2f}2 fractional digits3.14
{x:8.2f}width 8, right-aligned" 3.14"
{x:05d}zero-pad to width 500042
{x:,}thousands grouping1,234,567
{x:#x}hex with prefix0xff
{x:.1%}percentage25.0%
{s:^10}center in width 10" hi "
{s:*>6}*-fill, right-align"****hi"

F-strings are the idiomatic way to format in Dragon - prefer them over manual concatenation everywhere. Next, the raw byte view of text: Bytes and Encoding.