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 referencefit(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.

The same is true of animated attributes and reader-set parameters: if a value can change after compilation, reading it makes the expression bound, and there is no way to freeze it early. Identifiers and references covers the distinction between a reference and a constant in full, and Reading computed values in prose the judgement that follows from it.

#Every derived attribute

  • fitslope, intercept, r2. The slope is the fitted polynomial's derivative at zero: exact, and live under whichever degree is in force.
  • curvemax and min over the plotted domain.
  • integralvalue, the computed area.
  • parametervalue, the reader's current setting.
  • contour and heatmapmin and max over the visible window.
  • boxplotmedian, q1, q3, iqr, and the sample's min and max. A box summarising a pair of samples publishes each axis's under that axis's name — x-median, y-q3, im-iqr — and no plain median, which a sample of pairs does not have.
  • choroplethmin, max, mean and count over the values the join actually covered.
  • arcdistance, the great-circle length in kilometres.
  • parameter.timevalue, 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.