Skip to content

docs(language): the rules a call through a relation keeps, in one place - #537

Closed
FBumann wants to merge 4 commits into
mainfrom
claude/wonderful-brahmagupta-7pcnux
Closed

FBumann wants to merge 4 commits into
mainfrom
claude/wonderful-brahmagupta-7pcnux

Conversation

@FBumann

@FBumann FBumann commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Prompt: Explicitness wins. We stay with the current main and define some rules every notation needs to align to. We can revisit syntax if we want to afterwards again, under the defined rules — (the rules, agreed with @FabianHofmann, are the list below)

Note

The following content was generated by AI.

What this changes

dimensions.md gains the six rules, a table of what each end of a call may name, and a table of how far the rules reach. Docs only — the language is untouched.

The rules a call keeps

Six rules hold whatever a sum or an at looks like: what it computes, which operator is legal, and what survives an edit.

  1. The frame law. result = (operand − consumed) ∪ produced. The operand carries consumed ∪ joined, where joined = key − (consumed ∪ produced).
  2. The result gains a dimension only from the operand, never from an edit to the relation.
  3. Adding a value column is safe. A relation that gains one changes no existing call. A call names both of its ends, and a value column it does not name is not read.
  4. The key is fixed. A relation that gains or loses a key column is a different table. Every call through it joins on the key columns it named at neither end, so an edit to the key re-aims them all. Declare a new relation instead.
  5. An operand may grow. A call means the same when the operand gains a dimension the relation does not name. That dimension passes through to the result. Growing it into a dimension the call lands on is refused rather than silently joined: (produced ∩ operand) − consumed has to be empty.
  6. sum consumes a key column; at consumes none. Each is refused in the other's case.
operator over=, consumed into=, produced
sum any columns, at least one a key column any columns
at value columns only any columns
reading a relation rules that hold
sum and at all six
a partition — shift, sum_back, position 4, and 3 in part
a relation in a where none of the six

Rules 4 and 5 were already on the page as bullets and stay there too, because each carries the worked example and the rewrite a reader needs.

Why

These are the rules the loader already keeps, and a future syntax proposal is measured against them — which needs them written down first, or the next revisit re-derives them.

One gap the writing found

Rule 3 does not hold for partitions. within= is optional, and omitted it means every value column, so a relation that gains one regroups every shift, sum_back and position through it:

cal: { key: hour, values: day }          →  shift(p, along=hour, offset=1, edge=0, by=cal) groups by ('day',)
cal: { key: hour, values: [day, week] }  →  the same call groups by ('day', 'week')

The page says so and cites #538, which proposes making within= required when by= is present — the mechanism sum and at already use. That is a language change and is not in this PR.

Each rule checked against the loader
  • _sum_dims returns (inner - by.dimensions) | by.into and _at_dims returns (inner - by.into) | by.dimensions — the same law, since dimensions is the fine side and into the coarse one.
  • _check_joined holds the operand to the joined dims; _sum_dims and _at_dims hold it to the consumed ones. Together: the operand carries consumed ∪ joined.
  • _check_lands_clear refuses on sorted((produced & inner) - consumed), which is rule 5 verbatim. The − consumed term is what lets a relation into its own dimension through.
  • joined is built as the key roles in neither over= nor into=, so key − (consumed ∪ produced). The draft of rule 1 said "the keys not consumed in a sum", which holds for sum alone; the symmetric form reduces to key − consumed for sum and key − produced for at.
  • Rule 6 is Direction.is_function_read, which computes key <= produced | joined. Since a column is consumed, produced or joined, that holds precisely when no key column was consumed.
  • The selection table was run, mixed cases included: sum(x, by=gen_bt, over=[generator, bus], into=technology) consumes a key and a value together, and at(cap, by=gen_bt, over=bus, into=[generator, technology]) produces one of each. Both load.
Why rule 6 is one line

The fuller reading — sum lands on a value, at lands on a key — is the common case rather than the law, and as a rule it would refuse two calls the loader accepts:

sum(p, by=connection, over=generator, into=bus)    # lands on a key column: a bare relation has no values
at(cap_bg, by=gen_bt, over=bus, into=technology)   # lands on a value column: the key is covered by the join

What holds without exception is that at consumes only value columns, and that the key columns named at neither end are joined on. The section's prose above the block still teaches the common case, which is what prose is for.

Interaction with #494

The block says "a call" throughout, never "a walk", so it reads the same whichever way that PR goes. It is based on main, so if #494 lands first this needs a rebase for the section heading alone (### Walks → ### Directions), and nothing in the block itself moves.

Gates

pixi cannot be installed here — pixi.sh is refused by this environment's egress proxy — so the gates ran from a pip environment on Python 3.13.

mkdocs build --strict clean, which is what checks the new #where-the-rules-reach anchor and the link into #partitions; the remote intersphinx inventory was dropped, which the proxy also refuses. tests/test_docs.py: 33 passed. prettier and typos clean. The page is not generated, so no generator was involved.

Sentence length on the page: n 131, avg 16.8, median 16, 14 over 25 — against n 105, avg 17.0, median 17, 13 over 25 before.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NUhbXWGiyZF46wHp3fVnr2

`dimensions.md` states the frame law and the four invariants the notation is
held to, above the bullets that were already its consequences. Two of the five
were on the page (the key is fixed, a call brings the dims it lands on) and
stay in their bullets, which carry the worked example each needs; three were
nowhere.

The law is the one the loader computes, checked against it line by line:
`_sum_dims` and `_at_dims` both return `(operand − consumed) ∪ produced`,
`_check_joined` holds the operand to `consumed ∪ joined`, and
`_check_lands_clear` refuses on `(produced ∩ operand) − consumed`. The joined
set is `key − (consumed ∪ produced)`, which is symmetric: for `sum` it reduces
to `key − consumed`, for `at` to `key − produced`.

The block says "a call" throughout rather than "a walk", so it reads the same
whichever way #494 goes.

Sentence length on the page: n 117, avg 16.8, median 17, 13 over 25 — against
n 105, avg 17.0, median 17, 13 over 25 before, so the block adds no long
sentence.

`mkdocs build --strict` clean, `tests/test_docs.py` 33 passed, prettier and
typos clean. The page is not generated, so no generator was involved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUhbXWGiyZF46wHp3fVnr2
@read-the-docs-community

read-the-docs-community Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

Documentation build overview

📚 math-spec | 🛠️ Build #34636396 | 📁 Comparing d503973 against latest (f4c4ec0)

  🔍 Preview build  

1 file changed
± reference/language/dimensions/index.html

@FBumann FBumann added the area: relations relations and dimensions: the relation design label Sep 18, 2026
`sum` consumes a key column and `at` consumes none — the sixth rule, and the
only one that says whether a call is allowed rather than what it computes or
what survives an edit.

It is exactly the check in the code. A column is consumed, produced or joined,
so `key <= produced | joined`, which `Direction.is_function_read` computes,
holds precisely when no key column was consumed. `at` needs it true and `sum`
needs it false.

Stated as one line because the fuller reading — sum lands on a value, at lands
on a key — is the common case rather than the law, and the loader accepts two
calls it would refuse: `sum(p, by=connection, over=generator, into=bus)` lands
on a key column, because a bare relation has no value columns; and
`at(cap_bg, by=gen_bt, over=bus, into=technology)` lands on a value column,
because the key is covered by the join rather than by the result. Both were
run against the loader. The section's prose above the block still teaches the
common case, which is what it is for.

Sentence length on the page: n 118, avg 16.8, median 17, 13 over 25 —
unchanged from the previous commit but for the added sentence.

`mkdocs build --strict` clean, `tests/test_docs.py` 33 passed, prettier and
typos clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUhbXWGiyZF46wHp3fVnr2
…es reach

Two tables. The first says what `over=` and `into=` may name on each operator,
which is rule 6 and nothing else: `sum` consumes at least one key column, `at`
consumes value columns only, and either end may name any subset otherwise.
Both were run against the loader, including the mixed cases —
`sum(x, by=gen_bt, over=[generator, bus], into=technology)` consumes a key and
a value together, and `at(cap, by=gen_bt, over=bus, into=[generator,
technology])` produces one of each.

The second says which rules reach the other two ways to read a relation. A
partition moves no frame, so the frame law is silent; rule 4 holds with force,
and rule 3 holds only where the call writes `within=`. A `where` builds no
direction at all.

That last gap is a real one rather than a wording: `within=` omitted means
every value column, so a relation that gains one regroups every partition
through it. The page says so and cites #538, which proposes the fix.

Sentence length on the page: n 131, avg 16.8, median 16, 14 over 25 — against
n 105, avg 17.0, median 17, 13 over 25 before the rules block.

`mkdocs build --strict` clean, which is what checks the new `#where-the-rules-reach`
anchor and the link into `#partitions`. `tests/test_docs.py` 33 passed,
prettier and typos clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUhbXWGiyZF46wHp3fVnr2
…re the rules

The section opened on the mechanics of a call. It now opens on the division of
labour: a relation says which rows exist and what identifies them, and an
operator says what to do with the rows a coordinate finds. The key is the
hinge — consume a key column and many rows share what is left, leave it whole
and one row is found.

One table puts every use of a relation on that single axis: many rows added up
is `sum`, one row read is `at`, one row as a group label is a partition, one
row or none as an answer is a `where`. Only the first finds more than one,
which is why only `sum` adds anything up — rule 6 as the reader meets it
rather than as a law.

The table `Where the rules reach` listed the same four categories one screen
below, so it goes; its prose stays, since what it says about partitions and
`where` is not in the new table.

Sentence length on the page: n 136, avg 16.8, median 16, 15 over 25.

`mkdocs build --strict` clean, which checks the two links out of the new
table. `tests/test_docs.py` 33 passed, prettier and typos clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUhbXWGiyZF46wHp3fVnr2
@FBumann

FBumann commented Sep 20, 2026

Copy link
Copy Markdown
Contributor Author

Superseeded

@FBumann FBumann closed this Sep 20, 2026
@FBumann FBumann added the docs Documentation pages, guides, reference and README label Sep 24, 2026 — with Claude
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: relations relations and dimensions: the relation design docs Documentation pages, guides, reference and README

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants