Records, arrays, and bounds you choose taught the
container whose bounds are yours to pick. This page is about a different pair
of types — vector[3] of int64, matrix[2, 3] of float64 — that are not
containers first. They are math values with shape: the shape lives in the
type, products whose dimensions cannot meet are refused before the program
exists, and the operators they carry are the linear algebra a formula means.
The example is examples/VectorsAndMatrices
in the tutorial repository.
Getting the file
make -C examples/VectorsAndMatrices runVectorsAndMatrices
A, row by row
1 2 3
4 5 6
v 1 2 3
v + v 2 4 6
3 * (v + v) 6 12 18
minus v 5 10 15
A * v 14 32
Dot(v, v) 14
Transpose(A) 1 4 / 2 5 / 3 6
A * T 14 32 / 32 77
after b := v and b[0] := 99, v[0] is still 1The example, walked
Shape in the type
type
Vec3 = vector[3] of int64;
Mat23 = matrix[2, 3] of int64;The 3 and the 2×3 are facts of the type, not properties of a value somebody
must remember to check. Indexing runs from zero — A[r, c], v[i] — because
these are math values with one fixed indexing convention, where an array
lets you choose bounds precisely because it is your container and your
domain. An index a constant can prove wrong is refused at compile time; the
third experiment below reads that refusal.
Elementwise, and the scalar meet
u := v + v;
u := 3 * u;
u := u - v;+ and - work element by element between tensors of one shape, and a
scalar meets a tensor from either side — 3 * u and u * 3 mean the same
scaling. Integer literals adopt the element type; a float scalar must be
exactly the element type, because widening a whole tensor behind your back
is the kind of quiet cost this surface refuses to have.
The product family
w := A * v;
G := A * Transpose(A);* is the linear-algebra product: matrix times vector, matrix times matrix,
and chains of either — an operand may itself be an operation’s result. The
shapes must meet, and the compiler checks that they do; the first experiment
reads that refusal too.
What * is deliberately not is the dot product. Between two vectors it has
no meaning at all, and the refusal states the design in one breath — the dot
product is the named function Dot, a matrix quotient would smuggle a
hidden inverse, and a plain storage array joins no algebra. Transpose
answers the flipped matrix, a named verb like Dot, because a formula reads
best when its exotic moves have names.
The value rule, unchanged
b := v;
b[0] := 99;v[0] is still 1. Assignment copies the whole value, exactly as it copies a
record or an array — shape does not buy an exception to the one rule the
language keeps everywhere.
Where the family continues
- WYSIWYG slicing —
Row,Column,SubMatrix,Window: pieces of a shape as named copies. - Spans and the lending law — the borrowed view when a copy is the wrong cost, inside a lifetime the caller can see.
- Deterministic reductions —
Sum,Meanand their kin over these values, one documented pairwise tree, bit-exact serially and in parallel. Float tensors get their exactness story there. - Generics — shape-generic code: the
genclause ranges over dimensions as well as types, so one routine serves everyvector[N].
Try it
1. Make the shapes not meet. Change w := A * v; to w := A * w; — a
2×3 against a 2-vector — and rebuild:
analyzer error 5334: the product 'multiplication' requires the left operand's columns to meet the right operand's leading dimension: found 'Mat23' and 'Vec2'The dimension check most numeric code performs at run time — or forgets — happened before the program existed.
2. Multiply two vectors. Try s := v * u; with an int64 s:
analyzer error 5336: the operator 'multiplication' has no meaning on 'Vec3' and 'Vec3': tensors define '+', '-' elementwise and '*' as the linear-algebra product — the dot product is the 'Dot' function, a matrix quotient would smuggle a hidden inverse, and a plain storage array joins no algebraThe whole operator design, stated by the refusal itself. Write Dot(v, u).
3. Step past the shape. Write v[3] := 1; and rebuild:
analyzer error 5128: constant index expression at position 1 in selection operation 'index' has value '3' outside array index domain 'subrange[0..2] of int64'A constant the compiler can read is checked where it stands; a runtime index is trapped at its named line, exactly as the aggregates page promised for arrays.
What the compiler proved
Every dimension agreement in this program is a compile-time fact, every constant index is checked where it is written, and a copy is a copy under the one value rule. The arithmetic itself ran identically on x86-64 and ARM64 — and for float elements, the reductions page states the stronger promise: bit-exact results, serial or parallel.
Next
The math unit — the scalar verbs beside these types: generic over widths, constants as declarations, and the libm seam. Every example behind the series lives in the tutorial repository.