Wit

Interpolation & captures

Wit has two substitution mechanisms, and keeping them straight is the single most common point of confusion. Captures::name:: and the body slot ... — carry rich markup, whole blocks of content. Interpolation{{path}} — drops a scalar string into one narrow context. This page shows what each becomes and, crucially, where each is live.

Captures carry rich content

A captured value is not just a string — it is re-parsed as a full document at substitution time. So a captured value that is a bulleted list becomes a real list:

source
#wrap ||body||
::body::
wrap#

@wrap
  body:
    @ul
    @li One li@
    @li Two li@
    ul@
wrap@
result
  • One
  • Two

That renders a genuine <ul> with two items. The same holds for a heading (value-contains-heading becomes a real heading) and multiple paragraphs (multi-paragraph-value). At an inline position a single-paragraph value splices its inline content, while a multi-block value lifts into blocks (value-at-inline-position); an inline-only value takes a fast path (value-with-emphasis-only).

Interpolation is a scalar hole

{{path}} is a doubled-brace scalar hole. The path is [A-Za-z0-9_.-]+ (inner spaces allowed), and it resolves against iteration variables, data definitions, or a bare scalar def. A number, boolean, or null coerces to a string; a record or collection is E_UNRESOLVED_REFERENCE — you can only interpolate a scalar.

Where interpolation is live: only inside raw nodes

This is the rule to memorise: {{…}} interpolates only inside @@ raw-node bodies. It is perfect for CSS holes:

source
#theme: { accent - green }

@@style
.badge { color: {{theme.accent}}; }
style@@
result

That renders .badge { color: green; }. In ordinary prose, { is record-arg syntax, so a brace pair renders literally:

source
#book: { title - The Keeper }

The book {{book.title}} versus @book.title here.
result

The book {{book.title}} versus The Keeper here.

That outputs the literal text {{book.title}} next to the resolved The Keeper. A frozen @@@ body makes {{…}} inert too — see Literal & raw nodes.

Two outputs, one lookup

The same lookup feeds both mechanisms, but they emit different things:

  • {{name}} → a scalar string, for class names, colours, CSS holes — live only in @@ bodies.
  • ::name:: and ... → rich content, live only in a definition body.
  • Data into prose is @name.field — never {{…}}.

Common mistakes

  • Expecting {{book.title}} to interpolate in a paragraph — it renders literally; use @book.title.
  • Writing ::loopvar.field:: in prose — that is drift from older notes; use data access @loopvar.field.

See also

Defining nodes

· Data access · Literal & raw nodes · Form-fill (rich values)