Skip to content

Note that a tag's { } block binds to the last dotted segment#329

Open
mlennie wants to merge 1 commit into
malloydata:mainfrom
mlennie:monty/tag-brace-binding-note
Open

Note that a tag's { } block binds to the last dotted segment#329
mlennie wants to merge 1 commit into
malloydata:mainfrom
mlennie:monty/tag-brace-binding-note

Conversation

@mlennie

@mlennie mlennie commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What

# bar_chart.stack { y=total_sales } parses cleanly, sets y on stack where nothing reads it, and renders a chart that silently ignores the field you asked for. A user hit this in production last week and had to open the network tab to find out why their chart was empty.

The docs aren't wrong about any of this, which is worth saying plainly: there's no example of that form anywhere, and bar_charts.malloynb correctly shows # bar_chart.stack bare. The gap is that nothing states the binding rule where a chart author would meet it.

Two properties tables invite the mistake by naming every property in dotted form and then showing brace-form examples a few rows down, so welding the two reads as the natural way to set both at once:

  • bar_charts.malloynb: .stack# bar_chart.stack at :10, and .y# bar_chart { y=total_sales } three rows later. This is the one the user hit.
  • numbers.malloynb: the # duration table puts .terse directly above .number# duration { number="0.0" }, adjacent lines.

This adds one line to each.

Why not just link to the tag reference

language/tags.malloynb does document the shape, under advanced property syntax, but only for tName.p1=value { pp1=v1 pp2=v2 } — the form with a value. The trap is the value-less form, tName.p1 { ... }, which isn't shown. It also never says "last segment", and it's in the language reference rather than where someone reading a chart's properties is looking. So the note states the rule at the point of confusion and links out for the rest.

Verified

Both forms run through the real @malloydata/malloy-tag parser, because "looks right" isn't enough here:

form resulting tag tree
# bar_chart.stack { y=total_sales } bar_chart.stack.y — y buried under stack
# bar_chart { stack y=total_sales } bar_chart.stack + bar_chart.y
# duration.terse { number="0.0" } duration.terse.number — number buried under terse
# duration { terse number="0.0" } duration.terse + duration.number

Both trap forms parse without error, which is exactly what makes them a trap. The fix form's tree is the exact union of # duration.terse and # duration { number="0.0" }, so the two are provably equivalent rather than plausibly so.

npm run build-prod passes locally.

Scope

Only tables that pair a bare dotted example with a brace example for the same tag can produce the inference, which is these two. charts_line_chart.malloynb is all-brace with no bare-dotted example. overview.malloynb lists .stack and .terse, but its Key Properties column is a name list rather than usage examples, and its neighbouring brace examples are different tag roots, so there's nothing to weld — it links here anyway.

`# bar_chart.stack { y=total_sales }` parses cleanly and sets `y` on `stack`,
where nothing reads it, so the chart silently drops the field and renders
wrong. The same shape catches `# duration.terse { number="0.0" }`.

Both properties tables invite it: they name every property in dotted form,
then show brace-form examples a few rows down, so combining the two reads as
the natural way to set both at once. The rule is in the tag language reference
under advanced property syntax, but only for the `tName.p1=value { ... }` form,
and not where someone reading a chart's properties would find it.

State it at the point of confusion, in the two tables that pair a bare dotted
example with a brace example for the same tag.

Signed-off-by: Monty Lennie <montylennie@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant