Value primitives

Eleven constructs the compiler evaluates and folds into the text — rounding, separators and case — and why none of them can read a live value.

#Evaluated at compile time

Eleven micronodes are evaluated by the compiler rather than the renderer. Each takes a value as its body, computes a result, and splices that result into the surrounding text as characters. Nothing reaches the output but the text — no node, no identifier, nothing a renderer has to know about:

The book costs $!format:comma{{price}}(places: 2).

    compiles to the single run   The book costs $1,234.57.

That is the whole trade. The number is gone by the time the document renders: it cannot be re-formatted for another locale, animated, or bound to. What is bought for it is that the body is typed!round:2{banana} is a compile error rather than something odd on the page.

#Writing a fold

A fold is written like any other micronode, and its first attribute is the implicit one, so !round:2{} and !round{}(places: 2) are the same thing. It carries no #id — there is no node for one to attach to.

!round:2{3.14159}                  3.14
!format:comma{1234567}             1,234,567
!upper{hello}                      HELLO
!truncate:8{internationalisation}  interna…

Folds run wherever {{…}} substitutes — in prose, in an attribute value, in a directive value — and nest inside out, because a body is expanded before it is typed:

!format:comma{!round:2{{price}}}   1,234.57

A fold's output is never re-scanned. It is a value, not markup, so !upper can never turn data into structure. The two places folding does not reach are the ones whose job is to preserve a sample verbatim: the body of lines, and the $…$ of latex.

#round, ceil, floor, abs, clamp and format

  • round — to places decimals, half away from zero. !round{3.7} is 4.
  • ceil, floor and abs — up, down, and magnitude. No attributes.
  • clamp — held inside an interval: !clamp:0..1{1.4} is 1.
  • format — separators. comma groups in threes, percent scales by 100 and appends the sign, fixed uses places without grouping, scientific writes a mantissa and exponent.

A result renders as the shortest exact decimal — no trailing zeros, no exponent, -0 normalised to 0.

format emits separators and never a currency symbol. The compiler carries no locale table and never will, so the symbol belongs to the sentence: $!format:comma{{price}}(places: 2).

#upper, lower, title, truncate and pad

  • upper, lower and title — case, mapped locale-independently.
  • truncate — to a maximum length, ending in an ellipsis counted inside it. A length with no room for the ellipsis is an error.
  • pad — widened to length with a filler, at the start or the end. Lengths count Unicode scalar values.

There is no trim: text arriving in a fold has already been trimmed.

#Why a fold cannot read a live value

A binding has no value until the document renders, and a fold runs before that. So reading one is an error rather than an em dash in the output:

The fitted slope is !round:2{#f.slope}.

[reference]`round` is evaluated at compile time and cannot read the live value `#f.slope`. Wrap the reference in a library micronode that declares a `reference`-typed attribute instead

Rounding a live number is the renderer's job, and !value is where it is asked for — its digits, format and maths do for a live value what these eleven do for a fixed one:

The fitted slope is !value:#f.slope(digits: 2).
The two are opposites and it is worth keeping them apart. A fold is a number the author knows, written once and frozen; !value is a number the canvas knows, read every frame. Reading computed values in prose covers which one a given sentence needs.