# Computed and locked values

> Values that follow from others, kept right by recomputing only what a change reaches; and values frozen or held against change.

Some values are not entered but follow from others: a line's total from its
price and quantity, a date thirty days on, a label made of a name and a status.
They should always be right without anyone asking, and cost no more to keep
right than the change that moved them. Some values, entered or computed, must
not move at all once they are agreed.

## Best practices

**Recompute what a change reaches, and no more.** When a value changes, the
values computed from it are computed again, in the order they depend on one
another — and where one comes out as it was, what is computed from it is left
alone. A change to one field of a thousand records recomputes a thousand small
things, not the table.

**Make a computed value look computed.** It reads like any value, but is marked
as following from others, and says from what. Editing it is not the same as
editing what it is computed from, and people should be able to tell before they
try.

**Say what a cycle is, rather than show a wrong number.** Values whose formulas
lead round to themselves, and everything computed from them, hold nothing; each
says that it is in a cycle, and with what.

**Offer two kinds of lock, and say which is on.**

- _Frozen_ — a computed value keeps what it has and stops following what it is
  computed from, which may go on changing. Typing over a computed value freezes
  it at what was typed — but a paste or a fill that writes what it would come
  to anyway leaves it following. Unfreezing computes it again.
- _Held_ — the value may not change. A write to it is refused; and when it is
  computed, so is any change elsewhere that would move it. "The total is agreed
  at 10,000": changing a price or a quantity so that the total would differ is
  refused, and says it is the total that refused it. Changes that leave it as it
  was — a higher price and a lower quantity that come to the same — are allowed.

A value that is not computed refuses writes under either lock.

**Refuse the part, not the whole.** When a paste or a fill reaches locked
values, what may be done is done, what may not is left, and both are said: how
many were refused, and by what.

**Keep held values through a change of shape.** When the shape of the data
changes — a field dropped, its kind changed, an option removed — a held value
it would take blocks the change, and the values that block it are named. A
limit moved past a value takes nothing: the value stays, reported as out of
range, for someone to decide about. A field that is renamed keeps its values,
and its locks, under its new name.

**Undo it all together.** What a change computed is undone with the change; a
lock or an unlock is undone like any other action.

## Conventions

| Intent                   | Pointer                           | Keyboard                           | Touch            |
| ------------------------ | --------------------------------- | ---------------------------------- | ---------------- |
| See what a value follows | Hover the value                   | Focus it; it is in its description | Touch and hold   |
| Freeze a computed value  | Type over it, or the value's menu | Type over it                       | The value's menu |
| Hold a value             | The value's menu                  | The value's menu (Menu key)        | The value's menu |
| Unlock                   | The value's menu                  | The value's menu (Menu key)        | The value's menu |

## Behaviour

Each statement is verified by a test in Chromium, Chromium (touch), Firefox, WebKit, WebKit (touch), unless it names the environments it applies to or is marked open.

### Live

- Every computed field is computed, each after what it is computed from, however they were declared.
- A change recomputes only what is computed from it.
- A computed value that comes out as it was stops there: what is computed from it is not recomputed.
- When nothing computed changes, the record is the same record.
- A formula's nothing is `null`, however it was said.
- A dotted reference reads the field of each item of a list, and depends on the list.
- Fields in a cycle, and what is computed from them, are never computed: they hold nothing, and the plan names them.
- A field computed twice is a mistake in the caller.

### Changes to a list of records

- What a change brings with it is a change of its own, which applied after it makes every computed value right.
- Records put in are computed whole; records taken out bring nothing.

### Locks

- A frozen computed field keeps its value while what it is computed from changes, and what depends on it sees the kept value.
- A frozen computed field named as changed is computed again: that is how it is unfrozen.
- A write to a frozen computed field is its new kept value; what is computed from it follows.
- A locked field that is not computed refuses writes, under either kind of lock.
- A held computed field refuses a write to itself, and every write that would change it by way of what it is computed from.
- Writes to one record that between them leave a held value as it was are allowed together.
- A held value is held through a frozen one: what is frozen stops the change on its way.

### One record, as a form changes it

- A change comes with what it computes, and only what the written fields reach is computed again.
- A computed field written over that was not locked is frozen at what was written, and what is computed from it follows.
- A change locks refuse is not made at all, and the field that refused it is named.
- Unlocking a frozen computed field computes it again, unless that would change a held one.
- Any value can be held; a computed one that is not locked frozen; a frozen one held; and a locked one unlocked.

### Many records, as a table changes them

- Writes come with what they compute, as one change, recomputing only the records and fields they reach.
- A computed value written over is frozen at what was written; written with what it would come to anyway, it goes on following.
- What locks refuse is left and named, and the rest is done.
- Locks kept under records' places move with their records past records put in or taken out.

### Records within a record

- Every record within a record is made right, at any depth, and the record's own fields are left to its caller.
- Only what changed is looked into: a record within that is the same object is passed by.

### A schema that changes

- Records are made to fit it as a change that can be undone; a limit moved past a value changes nothing.
- A held value the change would lose — dropped, of another kind, an option gone — is named, for the change to be refused.

### Formulas in a schema

- A field with a formula is computed from the fields it names, lists of records included.
- A formula that cannot be read, gives what its field cannot hold, or depends on itself is a problem of its field, which is then not computed.

### Balancing

- A held computed value is kept by finding what another field must be: the total agreed, the quantity changed, the price makes up the difference.
- A value is given only if it keeps the held one exactly, made as the field holds it; else none.
- A formula that is not of the first degree is found too, if there is a value to find.

## Reference implementation

React: `npx shadcn@latest add https://ui.publictheta.com/r/derived.json`
