Derived attributes
Read-only values a construct computes while it renders, the constructs that publish them, and their refusal of substitution.
#Read-only, recomputed every frame
Some constructs compute something as they render — a fit's slope, an integral's area, the value a reader has dragged a parameter to. Those results are published as derived attributes: read-only, recomputed every frame, and never written by the author. They are listed in their own section on the reference page of any construct that has them.
#Reading a derived value
By member access on the construct's id, written as a {{…}} group:
The fitted slope is {{#f.slope}}.A reference written bare reaches one only where the attribute's declared type is reference — fit(of: #points), cue.trace(path: #flight), table(data: #islands). Everywhere else the text #f.slope is just those characters: a mark's label: is rich-text, so label: slope = #f.slope renders the literal words slope = #f.slope rather than a number.
The group works there, though. A rich-text attribute takes a bound expression exactly as prose does, whole or mixed into a sentence, so a mark may caption itself with a number it computes — including its own:
text(x: 0; y: 4; label: slope = {{round(#f.slope, 2)}})
curve#c(expression: sin(x); label: peaks at {{#c.max}})A bare reading shows about four significant figures. {{round(#f.slope, 2)}} fixes the decimals, and the text functions shape it further — !format:percent{ {{#f.r2}} } for a percentage. Whatever the sentence needs, it is done to the number in the expression rather than asked for as an attribute.
#Why it must stay a binding
Reading #f.slope makes the expression around it bound: it depends on something that does not exist until the construct renders, so the compiler cannot finish it. A snapshot spliced into the source before parsing — reading the value once, while compiling — would let the prose assert a number the picture beside it later contradicts, so there is no such form. The expression tree travels to the renderer instead and is evaluated there, every frame.
#Every derived attribute
- —
slope,intercept,r2. The slope is the fitted polynomial's derivative at zero: exact, and live under whichever degree is in force. - —
maxandminover the plotted domain. - —
value, the computed area. - —
value, the reader's current setting. - and —
minandmaxover the visible window. - —
median,q1,q3,iqr, and the sample'sminandmax. A box summarising a pair of samples publishes each axis's under that axis's name —x-median,y-q3,im-iqr— and no plainmedian, which a sample of pairs does not have. - —
min,max,meanandcountover the values the join actually covered. - —
distance, the great-circle length in kilometres. - —
value, the period the reader is standing on.
Each is published by the construct itself, from inside the renderer, once per frame. Nothing an author writes adds a derived attribute to a construct that does not declare one, and nothing sets one — an assignment is an error rather than a silent override:
fit#f(of: #points; model: polynomial; slope: 2)[attribute]node `fit`: 'slope' is derived and cannot be set
Existence is still proven at compile time: a reference to a derived name the construct does not publish fails before anything renders.