sysl

Guide Programs

Seventeen real programs written to force a language decision — what each one owns, and what writing it found.

The tour teaches the language, the reference says what the rules are, and the library says what ships beside them. This section is about something else: the seventeen programs in guide/ that were written to force a language decision.

They are not demonstrations of a finished language. That distinction is the whole of why they exist, and it is worth stating in the form the set’s own README does:

The value is in the friction. The point at which a program cannot be written cleanly is the point at which the language is wrong, and that is the signal — a program that goes in smoothly told us nothing.

So a guide program is chosen for an axis nothing else in the set covers, written until it either goes in cleanly or does not, and the place it snagged is the output. Most of the language features documented elsewhere on this site exist because one of these programs could not be written without them: Buf[T], Hash in the core catalogue, from_utf8, the repeat form [v; n], unit as a zero-sized type, trailing-operator continuation, ref bindings, sizeof and alignof, and the rule that a constrained subtype’s attributes speak the subtype. Each of those has a program that paid for it first.

Why they are documented here rather than left in the repo

Because the findings are the interesting part and they are buried in header comments. A program’s own file records what it found in the place the code that provoked it lives, which is right for somebody reading the program and useless for somebody wondering why the language is shaped this way. These pages pull each finding out and link it to the rule it produced.

The seventeen, and the axis each owns

In the order they were written, which matters — a finding must be discharged before the next program starts, so each one is written on top of what the last one settled.

programthe axis it owns
jsonrecursive ownership — a value that contains itself through &T
hashmapthe trait system under load — bounds, what they promise, and ownership at once
bytecodethe module system, and the set’s one end-to-end assertion
pngthe byte level — endianness, bit streams, checksums, somebody else’s format
fftan algorithm checked against its own definition
sha2generic arithmetic — one algorithm at two widths — and static tables
shapesdynamic dispatch — a collection whose element types are forgotten
schedulerOS shapes — a run queue, blocking and waking, &T graphs mutated through references
kernelthe same scheduler with no heap — a fixed table, indices for identity
datetimea conversion that can succeed twice — wall clocks, timelines, daylight saving
matrixan operator whose result is neither operand’s type
ringthe constrained-subtype surface — ranges, :: attributes, contracts, invariants
slabraw storage — reinterpreting bytes, sizeof/alignof, an intrusive free list
lispthe reference cycle — the shape a count cannot reclaim, and weak T as the instrument that measures it
tabletext measured for display — a column is as wide on screen as its widest cell, and both a byte count and a character count are the wrong unit
qsortthe C boundary in the direction nothing else goes — a C routine that calls back, and the trampoline it takes
simdthe register width as the variable — one solver body instantiated at four lanes and at eight, from a lane count that is an ordinary value parameter

Two pairs are written to be compared, and the comparison is the measurement: scheduler against kernel is what reference counting was buying, since the two produce byte-identical schedules from opposite implementations; and inside ring, one buffer keeps the fact of where the ring ends once and the other keeps it twice.

Running one

Each directory is a project root, so the files in it are the anonymous root module and any subdirectory is a module named by its path:

sbt "syslJVM/run run guide/json"

Each program checks itself. Every line it prints is either a -- section header or ok followed by what was checked, so a failure is a line that says otherwise. GuideTests runs each one and asserts that nothing failed, that the number of checks is the expected one, and that the sections ran in order — the count being what makes the first assertion mean anything, since a check that quietly stopped running would otherwise look like a check that passed.

What a self-checking run cannot check

A violated require, a broken invariant and a failed range check all trap, so a program demonstrating one would die rather than report it and the run would look truncated rather than failed. A run therefore asserts a refusal only through a total operation that answers instead of trapping.

The traps are asserted separately, in @test(should_trap) functions that live in the program’s own directory. Each runs in a process of its own and passes by not coming back, so a trap is an observation there rather than the end of the run — which is what lets a refusal be stated in sysl, beside the code it is about. guide/ring was the first to need this.

The discipline that goes with it: write every refusal beside the call that is not refused. A should_trap test passes for any failure at all, its own setup included, so alone it cannot tell “the contract fired” from “nothing worked”. One call over the line and one call up to it, and the difference between them is the contract.


Next: json — the first of the set, and the one that found the language had no way to build a string.

Pages

  • json — Recursive ownership — a value that contains itself through `&T`, and the program that found the language could not build a string.
  • hashmap — The trait system under load — bounds, what they promise, and ownership all having to agree at once.
  • bytecode — The module system, and the set's one end-to-end assertion — source in, bytecode out, run it, compare what it printed.
  • png — The byte level — three byte orders, four length units, two checksums, and a format someone else defined.
  • fft — A transform kept beside the definition it rearranges, and checked against it.
  • sha2 — Generic arithmetic — one implementation serving four hash functions across two word widths.
  • shapes — Dynamic dispatch — a collection whose element types are forgotten, and combinators that hold what they cannot name.
  • scheduler — OS shapes — a run queue, blocking and waking, priority inheritance, and `&T` graphs mutated through references.
  • kernel — The same scheduler with no heap — a fixed table, indices for identity, and a measurement of what references were buying.
  • datetime — A conversion that can succeed twice — wall clocks, timelines, daylight saving, and the operator whose result no row can name.
  • matrix — An operator whose result is neither operand's type — one type carrying three implementations of one trait.
  • ring — The constrained-subtype surface — and an invariant that found a redundant field rather than a bug.
  • slab — Raw storage — reinterpreting bytes as a typed pointer, `sizeof`/`alignof`, and a free list threaded through the free blocks themselves.
  • lisp — The reference cycle — the one shape a reference count cannot reclaim, with `weak T` used as both the cure and the instrument that measures it.
  • table — Text measured for display — a column is as wide on screen as its widest cell, and both a byte count and a character count are the wrong unit.
  • qsort — The C boundary in the direction nothing else goes — a C routine that calls back into sysl, and the trampoline, slice address and element size it takes.
  • simd — One kernel compiled for more than one register width — a lane count that is a value parameter, and the four things writing a constraint solver lane-wise turned up.

Search

Esc
to navigate to open Esc to close