# Things

This page is the "Things" section of the Voxlang language reference. It is generated from LANGUAGE.md in the Voxlang compiler's own repository, so it says what the specification says and nothing more.

A URL is the heading, lowercased, with punctuation dropped and spaces turned into hyphens. Each "##" section of the spec is a page under /docs/, and every heading inside it is a fragment on that page, so "File I/O" is /docs/file-io/ and "Reading a whole file" inside it is /docs/file-io/#reading-a-whole-file.

As data: https://vox-lang.dev/docs/index.json lists every section and every heading with its URL, https://vox-lang.dev/docs/search.json carries one entry per heading with its first sentence and keywords, and https://vox-lang.dev/docs/anchors.json maps every slug to the page it lives on. Search is also a JSON endpoint: https://vox-lang.dev/docs/search?q=<words> returns ranked results as JSON, no page load.

Source: LANGUAGE.md at bf2cba0, 2026-08-22. https://github.com/Vox-lang/vox/blob/bf2cba037976586c30d22a48e7b7246d347f54a1/LANGUAGE.md

[Reference](https://vox-lang.dev/docs/) > Things

Vox's eleven builtin types are the compiler's own composite values — a buffer is `[capacity][length][flags][data]` with `'s` reading a field at a fixed offset. A **thing** opens that same mechanism to the program: a user-defined composite value type, built from named fields, with every offset fixed at compile time. No vtables, no dispatch, no runtime component — a thing is a layout, copied, printed, and compared by the compiler the way a buffer is.

A thing is defined once, at the top level, and its name then works everywhere a builtin type keyword works: in declarations, parameters, and return types. A definition declares a type — it allocates nothing and emits no code, so the only output around it comes from the ordinary statements.

[`examples/delivery.vox`](https://github.com/Vox-lang/vox/blob/bf2cba037976586c30d22a48e7b7246d347f54a1/examples/delivery.vox) is a complete program built from two things of its own: it declares them, makes one with a manifest member, nests one inside the other, copies, prints, and compares them.

## Defining a thing

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

Print "defined".
```

The keyword is **`thing`**, and the verb is **`has`**. A definition has two kinds of entry:

- a **data field** — `a <type> called <name>`, with an optional `is <literal>` default;
- a **function member** — `a function called <name>`, the manifest (see [The manifest](https://vox-lang.dev/docs/things/#the-manifest) below).

A field without a default takes its type's zero value. `thing` is a keyword only inside this construct — everywhere else it is an ordinary identifier, exactly like `send`, so a variable may be called `thing`:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

a number called thing is 42.
Print thing.
```

A thing name may be a bare word (`point`) or a quoted multi-word name (`'bounding box'`), the same forms any identifier takes:

```
A thing called 'bounding box' has
  a number called width is 1,
  a number called height is 1.

a 'bounding box' called viewport.
Print viewport's width.
```

**Field types in v1.** A field may be `number`, `float`, `boolean`, `time`, or any **previously defined** thing (things nest to any depth — see [Nesting](https://vox-lang.dev/docs/things/#nesting)). `text`, `list`, `map`, and `buffer` fields are deferred: they carry references and would reopen the aliasing question value copy semantics (below) is designed to avoid.

### The article rule

`a`/`an` pairs with **types** and with values coming into being; `the` pairs with **known identifiers**. The rule is load-bearing in the surface syntax, so it is worth naming once:

- `A thing called point has ...` — a *type* comes into being, so `A`.
- `a point called origin.` — a value of that type comes into being, so `a`.
- `the point's 'placed at'` in a member definition (`To do the point's 'placed at'`) — `point` is a known identifier (the type, declared in the manifest), so `the`.
- `a point's 'placed at' with 1 and 0` — a *new point* comes into being from the maker, so `a`.

The same word, two articles, two meanings: `the point's` reads a known member; `a point's` calls a maker that brings a new point into being.

## Declarations and field access

A thing name is a type noun everywhere the builtin ones are, so every declaration form works. All three lines below declare a `point` and give every field its declared default:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

a point called origin.
Set origin's x to 3.
Set origin's y to 4.
Print origin's x.
origin's y is origin's y add 1.
increment origin's x.
Print "origin sits at {origin's x}, {origin's y}".
If origin's x is greater than 3 then,
    Print "the origin moved right".
```

A field is an ordinary expression and an ordinary lvalue everywhere either is allowed — read, `Set ... to`, bare assignment, increment, decrement, format-string interpolation, and a comparison in a condition all appear above. `Create` declares with defaults too, and a quoted variable name is read and written through the same possessive:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

Create a point called 'the far corner'.
Print 'the far corner''s x.
Set 'the far corner''s y to 2.
Print 'the far corner''s y.
```

A field with no default takes its type's zero. A `float` and a `boolean` field carry defaults; a `number` field with none is `0`:

```
A thing called 'water tank' has
  a float called 'depth in metres' is 1.5,
  a boolean called 'the pump is running' is true,
  a number called 'litres drained'.

a 'water tank' called cistern.
Print cistern's 'depth in metres'.
If cistern's 'the pump is running' then,
    Print "the pump is running".
Print cistern's 'litres drained'.
```

A thing declared inside a function is local to that function (its storage is the stack, not `.bss`):

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

To 'plot a point'.
  a point called cursor.
  Set cursor's x to 9.
  increment cursor's y.
  Print "the cursor sits at {cursor's x}, {cursor's y}".

'plot a point'.
```

## Nesting

A field may be a thing, so things nest to any depth. A nested thing contributes its own bytes inline, so a chained possessive is one sum of compile-time offsets — never a pointer chase — and the route's own `'route number'` sits after the whole nested segment:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

A thing called segment has
  a point called start,
  a point called end.

A thing called route has
  a segment called leg,
  a number called 'route number'.

a route called commute.
Set commute's leg's start's x to 3.
Print commute's leg's start's x.
increment commute's leg's end's y.
Set commute's 'route number' to 66.
Print commute's 'route number'.
```

Defaults apply recursively: a field whose type is a thing takes that thing's own defaults, written into the nested bytes at declaration, so a stamp carried by a letter begins life with its own defaults even though nothing initialises it:

```
A thing called stamp has
  a number called 'day sent' is 25,
  a number called 'cost in pence' is 12.

A thing called letter has
  a stamp called posted,
  a number called 'weight in grams' is 2.

a letter called invitation.
Print invitation's posted's 'day sent'.
Print invitation's posted's 'cost in pence'.
Print invitation's 'weight in grams'.
```

A thing containing itself — directly, or through other things — has no finite size, so the definition that closes the cycle is a compile error naming the chain:

```
A thing called ouroboros has
  an ouroboros called tail.
(compile error: ouroboros contains ouroboros
   no finite size)
```

Things are acyclic by **two mechanisms**. Within one file, the **defined-earlier** ordering rule makes a cycle unconstructible: a field type must be a thing defined above the line, so a thing can never name itself or a thing defined below it. Across files reached by `see`, the analyzer's registry DFS proves the merged registry is acyclic. The DFS is load-bearing: it is what keeps the merged, multi-file registry acyclic, and it stands as defence-in-depth alongside the within-file ordering rule.

## Value copy semantics

A thing is a value. Assignment copies the whole thing, and the copy shares nothing with the original — a thing's size is a compile-time constant, so a copy is a run of inline moves, no allocation and no pointer left aliased:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

a point called origin.
Set origin's x to 5.
a point called moved is origin.
Set moved's x to 9.
Print origin's x.
Print moved's x.
```

The three spellings of assignment — a declaration with an initialiser, a bare `is`, and `Set ... to` — are all assignment, so all three copy. A copy is deep by construction: a nested thing is just more bytes, so copying a letter carries its point along and neither half is shared:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

A thing called letter has
  a point called postbox,
  a number called 'weight in grams' is 2.

a letter called invitation.
Set invitation's postbox's x to 3.
a letter called reply is invitation.
Set reply's postbox's x to 7.
Print invitation's postbox's x.
Print reply's postbox's x.
```

The same is true across a call. A function receives a copy of a thing and hands one back by returning it; nudging the parameter cannot reach the caller's point, because the only way out is the `Return`, which copies into the caller's own storage:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

To nudged with a point called start.
  Set start's x to start's x add 1.
  Return a point, start.

a point called before.
The after is nudged of before.
Print before's x.
Print after's x.
```

`The after is nudged of before.` declares `after` from what the call returns, so a maker never has to have its type written twice. A whole nested thing read out of a segment is copied the same way:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

A thing called segment has
  a point called start,
  a point called end.

To nudged with a point called start.
  Set start's x to start's x add 1.
  Return a point, start.

a segment called span.
Set span's start's x to 40.
a point called 'the far end' is nudged of span's start.
Print 'the far end''s x.
Print span's start's x.
```

Because a thing is a whole shape, not a value, the things you cannot do to one are named rather than done to its first field. Assigning a single value to a whole thing, or stepping one with `increment`, is rejected with the field to write named instead:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

a point called origin.
Set origin to 5.
(compile error: 'origin' holds a whole point, so only a whole point can be copied into it
   A copy source is a variable holding a point, a field that holds one, or a call that returns one (plan 310 §5)
   To write one field instead, name it - point's fields are: x, y)
```

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

a point called origin.
increment origin.
(compile error: 'origin' holds a whole point, not a value
   A whole thing is copied, passed, and returned whole (plan 310 §5)
   its fields are: x, y)
```

Writing one field is what those lines mean: `Set origin's x to 5.` A thing also cannot be interpolated into a text initializer (a text initializer is a different sink from `Print`) or compared with a single value; see [Printing](https://vox-lang.dev/docs/things/#printing) and [Equality](https://vox-lang.dev/docs/things/#equality).

Printing a call's result directly needs a variable, because the result is a whole thing that must land in storage before it can be read:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

To nudged with a point called start.
  Set start's x to start's x add 1.
  Return a point, start.

a point called before.
Print nudged of before.
(compile error: A call to 'nudged' returns a whole point, which is not a value
   What a call returns is copied into a point)
```

The workaround is the inference form above — declare a scratch slot from the call, then print it:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

To nudged with a point called start.
  Set start's x to start's x add 1.
  Return a point, start.

a point called before.
The after is nudged of before.
Print after.
```

## Printing

`Print p.` walks the fields in definition order and recurses into the things they hold, map-style. Every field name is baked into the emitted program, so nothing is read from a descriptor and nothing is allocated. A quoted field name prints in the quotes it is written with, and a function member takes no part — it is the type's API, not its state:

```
A thing called point has
  a function called 'placed at',
  a number called x is 0,
  a number called y is 0.

A thing called segment has
  a point called start,
  a point called end.

A thing called stamp has
  a number called 'day sent' is 25,
  a float called 'cost in pounds' is 1.5,
  a boolean called 'first class' is true.

To do the point's 'placed at', with a number called x and a number called y.
  a point called plotted.
  Set plotted's x to x.
  Set plotted's y to y.
  Return a point, plotted.

a point called origin.
Set origin's x to 5.
Print origin.

a segment called span.
Set span's end's y to 2.
Print span.
Print "the span runs {span}".

a stamp called posted.
Print posted.

The corner is a point's 'placed at' with 3 and 4.
Print corner.
```

A whole thing interpolates into a format string under `Print` (`Print "the span runs {span}".`), because `Print` is the sink that renders the fields. A text initializer is a different sink — it builds its bytes in a buffer — and interpolating a whole thing there is rejected, naming the field to interpolate instead:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

a point called origin.
a text called note is "the point is {origin}".
(compile error: 'origin' holds a whole point, which only `Print` can interpolate
   Interpolate a field instead - point's fields are: x, y)
```

## Equality

`is` between two values of the same thing compares those same fields at the same depth; `is not` is its negation. Like printing, the comparison is written out by the compiler, so it recurses into nested things:

```
A thing called point has
  a function called 'placed at',
  a number called x is 0,
  a number called y is 0.

To do the point's 'placed at', with a number called x and a number called y.
  a point called plotted.
  Set plotted's x to x.
  Set plotted's y to y.
  Return a point, plotted.

a point called origin.
Set origin's x to 5.
a point called marker.
Set marker's x to 5.
If origin is marker then,
    Print "the marker is where the origin is".
Set marker's y to 9.
If origin is not marker then,
    Print "the marker has moved off the origin".
```

Two things of *different* types cannot be compared (`origin is span` is rejected — only two of the same thing have the same fields), and there is no ordering on a whole thing — `origin is greater than marker` is rejected, naming the field to compare instead:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

a point called origin.
a point called marker.
If origin is greater than marker then,
    Print "further along".
(compile error: 'origin' holds a whole point, which nothing puts in order
   Two things are compared for equality only (plan 310 §8)
   Compare a field instead - point's fields are: x, y)
```

## The manifest

A thing's callable API is declared in one place, the **manifest**: each `a function called <name>` entry names a member. The member is then defined with **`To do the <type>'s <name>`**, and `do` is a keyword only in that position — everywhere else it is an ordinary identifier. The member definition uses `the point's` (a known identifier) — the [article rule](https://vox-lang.dev/docs/things/#the-article-rule):

```
A thing called point has
  a function called 'placed at',
  a function called 'reflected through the origin',
  a number called x is 0,
  a number called y is 0.

To do the point's 'placed at', with a number called x and a number called y.
  a point called plotted.
  Set plotted's x to x.
  Set plotted's y to y.
  Return a point, plotted.

To do the point's 'reflected through the origin', with a point called original.
  a point called reflection.
  Set reflection's x to 0 subtract original's x.
  Set reflection's y to 0 subtract original's y.
  Return a point, reflection.
```

Function members take no storage, so layout, copy, printing, and equality see only the data fields.

**Every declared member returns its owner.** That is what gives the manifest a crisp meaning — it lists the functions that *produce or transform* the thing. A definition whose `Return` is not `Return a point,` is a compile error naming both lines:

```
A thing called point has
  a function called 'placed at',
  a number called x is 0.

A thing called 'grid square' has
  a number called column is 0.

To do the point's 'placed at', with a number called x.
  a 'grid square' called square.
  Set square's column to x.
  Return a 'grid square', square.
(compile error: A declared member returns its own thing: point's 'placed at' must return a point
   hands back a grid square
   A function that computes something else from a point is an ordinary function)
```

A function computing some other type from a point (like `'magnitude squared'`) is an ordinary global function, reached by the instance possessive, with no manifest entry at all. The owner-return check reads the body's `Return` lines, not the signature — so a member whose only `Return` sits inside an `If` is not wrongly rejected.

The manifest is checked both ways. A `To do` naming a member the manifest does not list errors at the definition, naming the entry to add:

```
A thing called point has
  a function called 'placed at',
  a number called x is 0,
  a number called y is 0.

To do the point's sparkle, with a point called original.
  Return a point, original.
(compile error: point does not declare sparkle - add `a function called sparkle` to the type
   Membership is declared in the thing's definition
   point declares: placed at)
```

and a declared member nothing defines errors at the type, where the promise was made:

```
A thing called point has
  a function called 'never written',
  a number called x is 0.

a point called origin.
Print origin's x.
(compile error: point declares 'never written' but nothing defines it
   To do the point's 'never written', with <parameters>.
   Return a point, <value>.)
```

A member is defined once: a second `To do the point's 'placed at'` errors at the second definition, naming the first.

## The three call forms

Three ways to call, one rule each.

**Free call** — the function's own name, unchanged, in the global namespace. `of`, `to`, `with`, and `on` all introduce arguments:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

To 'magnitude squared' with a point called corner.
  a number called 'x squared' is corner's x multiply corner's x.
  a number called 'y squared' is corner's y multiply corner's y.
  Return a number, 'x squared' add 'y squared'.

a point called origin.
Set origin's x to 3.
Set origin's y to 4.
Print 'magnitude squared' of origin.
```

**Instance possessive** — `receiver's 'member'`: sugar for `'member' of receiver`. The receiver fills the function's *first parameter*; any further arguments follow the call preposition. A field always wins over a function of the same name, because the [collision rule](https://vox-lang.dev/docs/things/#one-identifier-space) refuses that program rather than letting one shadow the other. A receiver is anything that names a whole thing, so a field holding one reads the same way:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

A thing called segment has
  a point called start,
  a point called end.

To 'magnitude squared' with a point called corner.
  a number called 'x squared' is corner's x multiply corner's x.
  a number called 'y squared' is corner's y multiply corner's y.
  Return a number, 'x squared' add 'y squared'.

To 'scaled by' with a point called corner and a number called factor.
  a point called scaled.
  Set scaled's x to corner's x multiply factor.
  Set scaled's y to corner's y multiply factor.
  Return a point, scaled.

a point called origin.
Set origin's x to 3.
Set origin's y to 4.
Print origin's 'magnitude squared'.
The 'tripled corner' is origin's 'scaled by' on 3.
Print 'tripled corner''s x.

a segment called 'the line'.
Set 'the line''s end's y to 12.
Print 'the line''s end's 'magnitude squared'.
```

**Type possessive** — `a <type>'s 'member'`: calls a member *declared in the manifest*. The article is `a` because a new thing comes into being. This is the only way to call a **maker** — a member whose first parameter is not the thing:

```
A thing called point has
  a function called 'placed at',
  a number called x is 0,
  a number called y is 0.

To do the point's 'placed at', with a number called x and a number called y.
  a point called plotted.
  Set plotted's x to x.
  Set plotted's y to y.
  Return a point, plotted.

The pin is a point's 'placed at' with 1 and 0.
Print pin's x.
```

A maker cannot be reached by the instance possessive (a receiver has nothing to fill), and the message says so rather than reporting the member as missing — the manifest does declare it:

```
A thing called point has
  a function called 'placed at',
  a number called x is 0.

To do the point's 'placed at', with a number called x.
  a point called plotted.
  Set plotted's x to x.
  Return a point, plotted.

a point called origin.
Print origin's 'placed at'.
(compile error: point declares 'placed at', but a receiver cannot reach it here
   'placed at' is a maker: its first parameter is not a point, so a receiver has nothing to fill
   Name the type instead: `a point's 'placed at' with <arguments>`)
```

A member whose first parameter *is* the thing gets both the type possessive and the instance possessive:

```
A thing called point has
  a function called 'placed at',
  a function called 'reflected through the origin',
  a number called x is 0,
  a number called y is 0.

To do the point's 'placed at', with a number called x and a number called y.
  a point called plotted.
  Set plotted's x to x.
  Set plotted's y to y.
  Return a point, plotted.

To do the point's 'reflected through the origin', with a point called original.
  a point called reflection.
  Set reflection's x to 0 subtract original's x.
  Set reflection's y to 0 subtract original's y.
  Return a point, reflection.

The pin is a point's 'placed at' with 1 and 0.
The opposite is pin's 'reflected through the origin'.
The 'opposite of the opposite' is a point's 'reflected through the origin' of opposite.
Print opposite's x.
Print 'opposite of the opposite''s x.
```

A member name belongs to its owner, not to the program: two things may each declare a `'placed at'`, and the two definitions compile under distinct internal names, so the same member name is fine on two different types:

```
A thing called point has
  a function called 'placed at',
  a number called x is 0,
  a number called y is 0.

A thing called 'grid square' has
  a function called 'placed at',
  a number called column is 0,
  a number called row is 0.

To do the point's 'placed at', with a number called x and a number called y.
  a point called plotted.
  Set plotted's x to x.
  Set plotted's y to y.
  Return a point, plotted.

To do the 'grid square''s 'placed at', with a number called column and a number called row.
  a 'grid square' called square.
  Set square's column to column.
  Set square's row to row.
  Return a 'grid square', square.

The 'marked square' is a 'grid square''s 'placed at' with 5 and 6.
Print 'marked square''s column.
Print 'marked square''s row.
```

`do` stays an ordinary identifier outside `To do the <type>'s`: `To do.` defines a function called `do`, and `do.` calls it.

## One identifier space

Type names, variable names, and function names share a single global identifier namespace. This is what makes `the point's` unambiguous: there is only one `point`. Reusing a name is first-come-first-served, and the second definition errors at its own line, naming the first — whatever kind the first was:

```
a number called point is 0.
A thing called point has
  a number called x is 0.
(compile error: 'point' is already defined as a variable on line 1
   identifier space)
```

The same error names a function, a parameter, a loop variable, an inferred variable, or another thing, whichever came first. A thing's own fields and members live in a separate per-type **member space** (a type owns one), so `point's x` and `segment's x` do not collide. The collision rule there is first-come-first-served too: the second definition of any name in a type's member space — a field, a declared member, or a global function whose first parameter is that type — errors at its own line, pointing at the first:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

To x with a point called corner.
  Return a number, corner's y.
(compile error: point already has a field called 'x', so a function taking a point cannot be called 'x' too
   point is defined on line 1. A type owns one member space)
```

## Definitions are top-level only

A thing is defined where a function is defined — at the top level. Its layout is fixed for the whole program and has no block scope, so a definition inside an `If`, a loop, or a function body is a compile error, and the message says to move it above the block:

```
To 'take a reading'.
  A thing called measurement has
    a number called degrees is 0.
  Print "taken".
(compile error: A thing is defined at the top level, like a function
   Canonical form: A thing called measurement has <fields>.
   Move the definition above the block it is written in)
```

The ordering rule is about the **definition**, not about an instance of it. A definition stands above every use of its name; an instance is an ordinary top-level variable, so it obeys the ordinary rule instead — "variables declared at top level are global and can be used inside functions", wherever on the page the declaration is written:

```vox fragment
To 'show it'.
  Print origin's x.      (reads the global declared below)

a point called origin.
```

## Cross-file definitions

A thing defined in one file is usable from another via `see` (the definition is parsed into the program the way a function is). The whole surface crosses the boundary: the type noun in a declaration, a field read and write, the manifest member reached by the type possessive, and a global function taking the thing reached by the instance possessive. The seen file arrives where the `see` is written, so the same defined-earlier rule that orders one file orders the pair — every use below stands after the definition it names.

```vox fragment
(./include/geometry.vox — the definition and the maker travel together)
A thing called point has
  a function called 'placed at',
  a number called x is 0,
  a number called y is 0.

To do the point's 'placed at', with a number called x and a number called y.
  a point called plotted.
  Set plotted's x to x.
  Set plotted's y to y.
  Return a point, plotted.

(An ordinary global function taking a point first, so the including file
 can reach it through the instance possessive as well as by name.)
To 'shifted east' with a point called start.
  Set start's x to start's x add 1.
  Return a point, start.
```

```vox fragment
(Consumer file — sees the definition above.)
see "./include/geometry.vox".

a point called origin.
Set origin's x to 11.
Print origin's x.

The corner is a point's 'placed at' with 3 and 4.
Print corner's x.
Print corner's y.

The 'shifted corner' is corner's 'shifted east'.
Print 'shifted corner''s x.
```

A type name is one identifier across the whole compilation: defining the same thing in two files reached by `see` errors at the second definition, naming the other file. The diagnostic reads `'point' is already defined as a thing on line 4`, then names the file that defined it first (`include/point_defined_elsewhere.vox`) and the rule (`identifier space`).

A `see` of a file that cannot be read is an error.

## `.lib` export of a thing is not yet supported

A `.lib` interface file names types by noun, and no noun spells a user-defined thing, so an exported signature that takes or returns a thing cannot be written. Ordinary compilation is unaffected; an exported library function whose signature mentions a thing is refused with a message naming the field and the canonical workaround — pass the thing's fields across the boundary instead. A library that exports a function `To 'nudged east' with a point called start.` — taking a point and, in the same case, returning one — is refused when compiled with `--shared`:

> takes a point ('start'), which a library interface cannot describe yet returns a point, which a library interface cannot describe yet A thing is a layout private to one compilation

The same source compiles fine as an ordinary program; the refusal fires only at the library interface, because the interface has no noun for a user-defined thing. The diagnostic names each crossing field and points at the workaround — pass `start's x` and `start's y` as separate values.

## Sentence consumption and multi-line definitions

A thing definition is a new place the [sentence consumption](https://vox-lang.dev/docs/basics/#sentence-consumption) rules bite. Its entries are comma-separated, and the construct closes on a **period** or a **blank line**, the same termination rules every other construct follows: a period closes the entry list, and a blank line force-closes it (along with anything else still open). Indenting the entries is conventional but not required — the commas and the terminator carry the structure.

## Definition diagnostics

The definition construct creates a family of sentences that are never valid Vox. Each gets a targeted error stating the intent it recognises and naming the canonical form.

```
Create a thing called point.
(compile error: A thing is defined, not created as a variable
   Canonical form: A thing called point has <fields>.)
```

```
A thing called point is 5.
(compile error: 'is' declares a variable; a thing definition uses 'has'
   Canonical form: A thing called point has <fields>.)
```

```
A thing called point has.
(compile error: A thing needs at least one field
   Canonical form: A thing called point has)
```

A definition listing only manifest entries describes a zero-byte thing, so v1 requires at least one data field:

```
A thing called point has
  a function called 'from polar'.
(compile error: A thing needs at least one field
   `a function called <name>` declares callable API, not storage.)
```

A field default must be a literal of the field's own type — a computed value belongs in a function that returns the thing:

```
A thing called point has
  a number called x is 1.5.
(compile error: Field 'x' of thing 'point' is a number, but its default is a float
   literal of the field's own type)
```

Declaring with an unknown type name keeps the existing unknown-type error, extended to suggest near-miss **user-defined** type names alongside the builtins:

```
A thing called point has
  a number called x is 0,
  a number called y is 0.

a poimt called origin.
(compile error: Unknown type 'poimt'
   did you mean
   point)
```

## Type predicates and the runtime tag

User-defined things are not in the runtime tag system in v1. The type nouns `is a <type>` recognises are the builtins (`number`, `text`, `decimal`, `boolean`, `list`, `map`); there is no `is a point` yet, and a `list` or `map` of user things, or a `value` holding one, is likewise deferred. Things live in the compile-time type table, not the runtime tag.
