Scientific programming in Modula-9 — first steps

↖ contents

9 — Preparing data for plotting

The end of most analyses is a figure, and figures are where reproducibility quietly dies: a plotting daemon, a font cache, a library version, and yesterday's PNG differs from today's for no scientific reason. Plot renders SVG as a STRING — pure computation, deterministic to the byte: the same program produces the same figure, byte for byte, on any machine, and this page's own build verifies exactly that for the figure below. A figure you can cmp is a figure you can trust in CI.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
C9Plot.m9

expected output:

wrote /tmp/damped.svg, 7847 bytes

the damped oscillation, exactly as the program wrote it

The API is the smallest one that earns its keep: ClearFigure, AddLine (xs, ys, colorIndex, label) up to four labelled series, Render (pool, title, xlabel, ylabel) — axes scaled and ticked with the same "nice step" rule everywhere, NaN points simply breaking the line (a gap in the data is a gap in the plot, per chapter 5). RenderHeat does the same for a matrix. The result is a string because chapter 5 already gave us the file boundary: Io.WriteFile, one checked call.

Run here, the figure appears as a link under the output — the sandbox keeps what a cell writes to its /tmp and serves it back. Run locally, open /tmp/damped.svg in any browser. Either way there is no step in which a plotting server, a GUI toolkit or a locale was consulted; the bytes are the bytes.

Where you are now

Eight programs ago the language was a stranger. What you have seen: programs whose imports, widths, failure modes, ownership and thread discipline are all in the source and all checked; a CSV reader that believes the file's documentation instead of guessing; statistics with scipy behind every digit; timeseries whose resolution and convention are data; a chunked-store reader whose use-after-close is uncompilable; and figures that diff. None of it was convenient to write. All of it is convenient to REVIEW — and review, not writing, is where scientific software spends its life.

The reference for every module these chapters used is generated from the definitions themselves (m9c --doc) and ships with the compiler: DynStr, Io, Fmt, Time, Csv, Stats, Mat, Math, Frame, ZarrStore, NetCDF, Parquet, Plot and the rest. The repository's museum/ directory is the language's design rationale in executable form. And every example you just read runs, verbatim, on every commit — if this page and the compiler ever disagree, the build breaks before you can read the lie.

← Previous: data through zarr · Next: a real dataset →