Attributes

The parenthesised group every construct may carry: how values are written, and the types that parse them.

#The attribute group

Attributes live in a parenthesised group, written after the body and detail. On one line they separate with semicolons; across several, one per line with no separator. The two forms are equivalent, and a group may be empty — () — which is how a bare node head stops being prose.

point(x: 1; y: 2; colour: orange)

canvas.graph{
    curve(expression: x^2)
}(
    x-axis-label: t
    y-axis-label: height
)

The control-flow directives are the one exception to the group coming last — @if(audience: teacher){} and @for(n: 0..4){} put theirs first.

#Values as text, parsed by type

Every value is text until the attribute it lands in parses it, and a value the attribute's type cannot parse is a compile error — nothing is coerced or quietly defaulted. A type may be narrower than its name suggests — a number restricted to whole values, or to a range, or a list held to a fixed length — and a refused value's error names the restriction it broke rather than just the type. The type reference lists what each type accepts.

This is also why a dataset column needs no type of its own: a cell reaches an attribute as text and is parsed there, failing exactly as the same text typed by hand would.

#Shorthands & derived attributes

Two pages hang beneath this one. Attribute shorthands covers the four ways an attribute is written shorter than key: value — the implicit :value, bare booleans and ! negation, spaced names, and aliases. Derived attributes covers the attributes a construct computes while it renders — read-only, reached with #id.name or !value, never set.