internal design
Design goal
immut and mutable check the same preconditions (squareness, bounds,
compatible shapes) and must report them identically. internal keeps one
implementation of each check, so the two packages cannot drift apart, without
exposing the helpers to downstream code.
Mathematical background
Each guard is the characteristic test of a domain from the error design: , , and so on. Writing the test once as a total function gives both forms of a partial operation:
Deriving the aborting guard from the checked one (ensure_x matches on
ensure_x_checked) makes the two agree on every input by construction.
Design decisions
An internal package
MoonBit’s internal-package rule restricts imports of
Luna-Flow/linear-algebra/internal to packages of this module. The helpers
can therefore change freely, while their effects are specified on the public
methods.
HasShape separate from MatrixShape
@algebra.MatrixShape has the same method, but algebra is experimental and
the concrete packages should not depend on it. HasShape gives the guards a
bound that immut and mutable implement locally. The repetition is the cost
of keeping the stable concrete packages independent of the experimental layer.
Row before column
ensure_index_in_bounds checks the row first, then the column, and each
against its own dimension. Checking the flat offset against
instead would accept a column index that spills into the next row.
Correctness and invariants
ensure_x(m)aborts if and only ifensure_x_checked(m)returnsErr.- Each checked guard returns the error kind listed on the API page, with a fixed message.
- All guards run in and only call
shape.
Alternatives rejected
- Duplicating the checks in each package, which is how bounds behaviour
diverged before the
0.4line. - Public helpers. They would become API that downstream code depends on.
Boundaries
internal checks shapes and indices only. It contains no arithmetic, no
storage, and no checks that depend on values (singularity, emptiness of data,
convergence).