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
fit—slope,intercept,r2. The slope is the fitted polynomial's derivative at zero: exact, and live under whichever degree is in force.curve—maxandminover the plotted domain.integral—value, the computed area.- The controls —
slider,selectorand their cue twins —value, where the parameter currently stands: the reader's setting, or the scene's sweep where they have not moved it. Aselectorwhose values are names publishes the name itself. contourandsurface—minandmaxover the visible window.boxplot—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. A box grouped by a column of names publishes the plain ones only while it holds one group: each name has its own median, so a number prose needs to cite is drawn as its own box,on:its name.choropleth—min,max,meanandcountover the values the join actually covered.arc—distance, the great-circle length in kilometres.
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.