Every builtin word, type, and syntax form — plus a map of the machines.
Case-insensitive; convention is PascalCase for words and types,
camelCase for variables. Indexes are 1-based: counting starts at 1.
Everything is immutable — values never change; "update" words return new values.
Every word on this page is built into the mill. The standard library (the
machines, brought in with Include) is mapped in
The machines below; each machine’s full word
reference lives on its own page.
Program structure
Form
Meaning
Def Name(p As t, ...) As r
Function, expression-syntax body. Last value = result; no RETURN. Implicit binding of parameters.
Def Name ( a b -- c )
Function, concatenative (stack) body; the signature is documentation. ( a b -- c ) reads: takes a and b off the stack, leaves c — inputs before the dashes, outputs after.
Def Main()
Entry point.
Type Name ↵ fields
Record declaration: indented field As Type lines. Declare before use.
Type Name = v1(f, ...) | v2
Sum type: each variant is a constructor/pattern. Bare variant = zero fields (construct as v2()).
Include "FILE.SHODDY"
Include-once, left margin. Resolved relative to the including file, then SHODDYLIB, then the machine library beside the running mill. A file from a machine library is a machine: you get the words it declares, not the ones it includes in turn, and it is built if it has no DLL. One of your own files is spliced flat.
Include "FILE.SHODDY" As Q
Everything that file declares gets the prefix Q — and nothing it includes in turn, so a name never depends on its dependencies' include lists.
Name In Q
The namespaced name — collapses to the single word QName. Only needed when a bare name is ambiguous between two namespaces.
Redef Name(...)
Def with the duplicate/builtin check waived. Without it, redeclaring a name or shadowing a builtin is an error.
Rem ... or ' ...
Comment to end of line.
F(a,↵ b)
Line continuation: unclosed ( { [ pulls the next line in. Continuation indent is free; parenthesize any long expression to wrap it.
Let x = expr
Immutable binding; one per name. At the left margin: a program-wide constant, evaluated once before Main (the outermost scope).
Indentation is structure. A line with an unclosed bracket
continues onto the next physical line (continuation indent is free);
a statement may also continue on deeper-indented lines (pipelines).
Reserved characters, never in names: ! % # $ ? ; \
Operators need spaces around them: count + 1, not count+1.
Types
Type
Notes
Number
A 64-bit floating-point number (what most languages call a double; IEEE); the only numeric type. Exact for whole numbers up to 253.
String
Immutable. Escapes: \" \\ \n \t \r
Boolean
True / False. Never a number.
Quotation [ t -- u ]
Function value; closes over its environment (lexical) — it remembers the variables from where it was written.
List Of t
A cons list — a linked list; literal { 1, 2, 3 }. Walk it front to back (recurse).
Array Of t
Fixed length; Nth jumps straight to position k (O(1)). Index into these.
records (Type)
Nominal — matched by type name, not by shape; constructor + accessors + With + Case patterns.
Annotations are parsed, but checked only when the program runs. Type variables: single lowercase letters (t, u).
Operators (loose → tight)
Level
Operators
Notes
1
Or
Short-circuit in expression bodies: the right side runs only if the answer is still open. Strict words in stack bodies: both sides have already run.
2
And
3
= <> < > <= >=
Numbers or strings; = / <> also booleans, records (structural), arrays.
4
+ - &
& is string concatenation.
5
* / Mod
Division / Mod by zero abort.
6
^
Groups from the right (right-assoc); -n ^ 2 = -(n^2) (classic BASIC).
prefix
Not, unary -
Conditionals & matching
If cond Then Select Case expr
result Case 0 ' equality
Else Case 1, 2, 3 ' any-of
other Case 4 To 10 ' inclusive range
Case Is > 10 ' comparison (Is optional)
Ifte(c, t, f) ' inline Case Person(n, a) Where a < 18 ' destructure + guard
Case Order(Person(n, _), it, t) ' patterns nest; _ = ignore
Case Else ' default (else non-match aborts)
Both are expressions: each branch/clause block yields the value.
The tested value is evaluated once. Pattern names bind for guard and body; sub-patterns
nest to any depth. Case Type(...) is always a pattern; equality vs
a record: Case = Type(...) (or bare Case None for a
zero-field variant).
Function values
Form
Meaning
Name
Bare non-local name in argument position is passed, not called: Map(xs, Square)
Parenthesize to force a bare name to evaluate instead of quote.
The big four (+ friends)
Word
Meaning
Map(xs, f)
New sequence, f applied to each. Keeps input's kind.
Filter(xs, p)
Keep items where p is True.
Fold(xs, z, f)
Combine items into one value, left to right, starting from z (a left fold): Fold(xs, 0, +) sums.
Each(xs, f)
Run f per item (for effects).
Times(n, f)
Run f n times.
Range(a, b)
List of a..b inclusive.
Lists & arrays
Word
Meaning
{ a, b, c }
List literal (elements are expressions).
First / Rest / Prepend(x, xs) / IsEmpty
List recursion kit (First/IsEmpty also arrays).
Nth(xs, k)
k-th item; instant on arrays (O(1)).
SetNth(xs, k, v)
Copy with position k = v.
Length / Reverse / Concat
Both kinds (Concat: same kind).
Sort(xs)
Ascending; all numbers or all strings. Keeps input's kind.
ToArray / ToList
Convert.
Dim(n, v)
Array of n copies of v.
Records
Form
Meaning
Person("ann", 34)
Constructor, positional (arity checked).
Person(Name = "ann", Age = 34)
Constructor, named fields.
Age(p)
Accessor: every field name is a function. Dispatches on the record's type.
With(p, Age = 35, ...)
Copy with fields changed.
P1 = P2
Structural equality: equal when all fields are equal.
Strings
Word
Meaning
&
Concatenate.
Len(s)
Length.
Left / Right(s, n)
First / last n chars.
Mid(s, START, n)
n chars from START (1-based).
Instr(s, SUB)
Position of SUB, 0 if absent.
InstrFrom(s, SUB, FROM)
The same, searching from FROM
onward without copying the tail. A FROM past the end is 0, not an
error.
Upper / Lower(s)
Case conversion.
Str(n) / Val(s)
Number ↔ string. Val stops the program
on a non-number.
IsNumeric(s)
True exactly when Val(s) would succeed.
ValOr(s, FALLBACK)
Val that hands back FALLBACK instead of
stopping.
Chr(n) / Asc(s)
Code ↔ character.
CodeAt(s, k)
Code of the k-th character. Refuses past the
end, where Mid would trim — this is how you read a character, not
Asc(Mid(s, k, 1)).
Codes(s) / FromCodes(a)
Whole string ↔ Array Of
Number, exactly. FromCodes can build a NUL, which Chr cannot.
Split, Join, Trim, Replace, StartsWith, EndsWith, StrRep
— and IsBool/ToBool/ToBoolOr/ToBoolOpt, PadLeft/PadRight/PadZero,
ToFixed, Commas: str.shoddy. All of those are
literal; matching on a stated rule instead — RxRead, RxFind, RxFindAll,
RxReplace, RxSplit, RxQuote — is
regex.shoddy. Every error and
warning message, with causes and fixes:
the errors page.
Absence and failure
Predeclared by the language. No Include, and no accessor
words — Select Case is the only way in, so the check
cannot be skipped.
Some(v) / None()
Type Option = Some(Value) | None. Absence when the only possible reason is "not there".
Ok(v) / Err(why, at)
Type Result = Ok(Value) | Err(Why, At). Failure the caller could act on. At is 0 when there is no position.
Case Some(x) / Case None
Matching. Building an empty variant takes parentheses, matching one does not.
Some(1) = Some(1)
True — variants compare structurally, and None() = None() too.
There is no Value(o): an accessor on a sum type
is partial — defined for one variant only — so it would compile and then fail at run time on the very case
the type exists to make you handle. Pick Option when the reason
is always the same and Result when it isn't.
Math
Word
Meaning
Abs Min Max
Sqr(n)
Square root (negative aborts).
Floor Ceil Round
Sin Cos Tan Atn
Radians.
Exp Log
Log is natural log.
Pi()
3.14159... — zero-argument function, not a bare constant.
Negate
Sign flip (what unary - compiles to).
Sgn(n) / Fix(n)
Sign (-1, 0, 1) / truncate toward zero.
Asin Acos Atn2(y, x)
Inverse trig; Atn2 recovers the full-circle angle.
Tanh(x)
Hyperbolic tangent — saturates to ±1, never overflows.
Log10(n)
Base-10 log.
Wrap(a, b)
Floored modulo: result carries b's sign (angles, indexes).
Erf(x)
Error function — the curve behind normal (bell-curve) probabilities.
GammaP(a, x)
Regularized lower incomplete gamma — the curve behind the chi-square test's probabilities (its CDF).
BetaI(a, b, x)
Regularized incomplete beta — the curve behind the t and F tests' probabilities (their CDFs).
Rnd
Uniform [0,1): evenly spread from 0 up to, not including, 1. Impure.
Seed(n)
Make Rnd reproducible.
There are no bitwise operators and no integer
type. Number bases, bit operations, masks and fields, two's complement, the
codes and truth tables are all words in
bool.shoddy.
Time
Word
Meaning
Ticks()
Monotonic (only ever counts up) milliseconds, fractional — for measuring, never wall time.
Any value + newline. Effectful — it acts on the world outside the program; keep in Main.
Input(prompt)
Print prompt, read a line → String.
InputLine(prompt)
As Input, but → a 2-element Array [atEof, text]: Nth(r, 1) is True only at genuine end of stream, Nth(r, 2) is the line. Tells EOF apart from a blank line, which Input cannot.
InKey()
One pending keystroke → String, "" if none. Non-blocking, no echo; arrows and PF1–4 arrive as VT100 application-mode sequences for EvalKey.
Error(MSG)
Abort with message + line number.
Assert(COND, MSG)
Abort unless COND.
ReadFile(path)
Whole file → String (missing file aborts).
TryReadFile(path)
ReadFile that reports failure instead of aborting → Result: Ok(text), or Err(why, 0) with why = CANNOT READ 'path' (…) ending NO SUCH FILE / IS A DIRECTORY / ACCESS DENIED / UNREADABLE. The only way to ask whether a path can be read.
WriteFile(path, s)
Write, overwriting.
TryWriteFile(path, s)
WriteFile that reports failure instead of aborting → Boolean. The only way to ask whether a path can be written.
AppendFile(path, s)
Append.
FileExists(path)
→ Boolean.
DeleteFile(path)
Remove the file.
TryDeleteFile(path)
DeleteFile that reports failure instead of aborting → Boolean. True if the file went; False if it was not there or could not be removed. FileExists narrows that window without closing it — a file that exists can still be locked when the delete lands.
Args()
→ List Of String; program's command-line arguments.
1-based byte positions; GET/PUT advance the position. Fixed field
sizes make record offsets computable: Name(12) + Score(8) = 20-byte records,
record k at 1 + (k-1)*20.
Word
Meaning
BOpen(path)
→ handle. Read/write, creates if absent; up to 16 open.
TryTcpRequest(host, port, secure, msg)
The whole request/response as one guarded word: connect, optionally TLS, send, read to the end of the reply, close → Result. Ok(reply), or Err(why, 0) with why = CANNOT REACH 'host:port' (…) ending NETWORK IS DISABLED / NO SUCH HOST / REFUSED / TIMED OUT / TLS HANDSHAKE FAILED… No socket is ever visible, and it cannot hang — the read is deadline-bounded, unlike net.shoddy's RecvAll.
TryScribblerOpen(w, h)
ScribblerOpen that reports failure instead of aborting → Result: Ok(slot), or Err(why, 0) with why = CANNOT OPEN A WINDOW (…). Answers a slot number, not a window, so a reckoner seed can keep one in its named resource table — an ordinary program takes the window from ScribblerOpen and never sees a slot.
ScribblerOf(n)
The window a slot names. For the seed that holds the slot; aborts on one that is not open.
ScribblerShut(n)
Close the window a slot names and free the slot → Boolean. False for a slot already let go, so closing twice cannot end the run.
NetAllowed()
→ Boolean; whether the mill was started with --allow-net. The one word in the socket family that is not gated — the question has to stay askable when the answer is no, since every other TCP word aborts when the network is off.
TryTcpConnect(host, port)
TCPConnect that reports failure instead of aborting → Result: Ok(handle), or Err(why, 0) with why = CANNOT REACH 'host:port' (…) ending NETWORK IS DISABLED / TOO MANY OPEN SOCKETS / TIMED OUT / NO SUCH HOST OR REFUSED. Lets a word connect, ask and close inside its own body without a mistyped hostname ending the run.
TryBOpen(path)
BOpen that reports failure instead of aborting, and opens an existing file only → Result: Ok(handle), or Err(why, 0) with why = CANNOT OPEN 'path' (…) ending NO SUCH FILE / IS A DIRECTORY / ACCESS DENIED / UNREADABLE / TOO MANY OPEN FILES. The only way to pre-flight a binary read: BOpen creates the file when it is missing, so asking with BOpen changes the very thing you asked about.
Reading past end of file aborts. All effectful, like Print.
Record-at-a-time helpers — GetRec, PutRec, AppendRec, AllRecs:
recio.shoddy. The same words also serialize
whole values — a header (magic, counts), then the payload — as in
neural.shoddy’s NetSaveBin/NetLoadBin;
guide chapter 10 teaches both
uses.
TCP/IP sockets — gated (--allow-net)
Handles are Numbers, like file handles (up to 16 open). Payloads
are Strings, one byte per character (the Latin-1 convention). Every readiness op
is non-blocking: Recv yields "" when nothing has arrived, Accept
yields 0 when no client waits — poll (optionally around Sleep).
Off unless the mill is run with --allow-net.
Word
Meaning
TCPConnect(host, port)
→ handle. Connects (bounded 10s handshake); aborts if unreachable.
TCPListen(host, port)
→ handle. Binds an IP literal ("127.0.0.1" for loopback, "0.0.0.0" for any).
TCPAccept(h)
→ connection handle, or 0 if none is waiting. Non-blocking.
TCPSend(h, s)
Send all bytes of s.
TCPRecv(h, max)
Up to max bytes → String; "" when nothing pending or the peer closed.
TCPEof(h)
→ Boolean. True once the peer has closed (tells "" apart from "no data yet").
TCPPoll(h)
→ Boolean. Would Recv/Accept find something now?
TCPPeer(h)
→ remote "ip:port", or "".
TCPClose(h)
Shut down and close.
TCPSecure(h, host)
Upgrade a connected socket to TLS in place; host drives SNI and certificate name validation. A secured handle then reads blocking and TCPPoll refuses it.
All effectful, like Print. Friendly client/server helpers —
Connect, SendLine, RecvAll, Request, HttpGet, Listen, Serve1:
net.shoddy.
Scribbler — graphics window
A pixel buffer in a window; y grows downward, colours are r,g,b
0–255. Every word returns the scribbler (or its answer) — rebind
Let sc = ... and chains read functionally. Nothing shows until Blit.
The window needs mill run/mill dap; ScribblerOpen aborts
under bare dotnet.
Word
Meaning
ScribblerOpen(w, h)
→ handle; opens the window.
ScribblerClose(sc)
Close (idempotent — closing twice is safe).
ScribblerTitle(sc, s)
Window title.
ScribblerPixel(sc, x, y, r, g, b)
The one drawing primitive.
ScribblerGetPixel(sc, x, y)
→ 3-element Array r,g,b (out of bounds reads 0,0,0).
ScribblerFill(sc, r, g, b)
Flood the whole buffer.
ScribblerText(sc, x, y, scale, r, g, b, s)
8×8 font text, integer scale.
ScribblerWidth / ScribblerHeight(sc)
Buffer size.
ScribblerBlit(sc)
Push the buffer to the window — draw whole frame, blit once.
ScribblerPoll / ScribblerWait(sc)
→ raw 8-element event array (kind 0 = none; Wait blocks at zero CPU). Use NextEvent/PeekEvent instead.
ScribblerSetInterval(sc, ms)
Tick events every ms (0 = off) — prefer SetFps.
ScribblerSave(sc, path)
Write the buffer to path as a PNG. Consults no window and does not blit — saving and showing are separate requests. Alpha is dropped.
ScribblerPlace(sc, x, y)
Ask for the window's top-left corner at desktop pixel x, y. A request, not a command.
Typed events (ScribblerEvent, NextEvent, PeekEvent, SetFps) and
drawing words (DrawLine, FillRect, FillCircle, DrawPolygon, FloodFill):
scribbler.shoddy.
Sound
Channels 1–8. Never blocks, never fails the program: audible
under mill run/mill dap; silent no-op headless (woven
output via bare dotnet, no audio device). Bad arguments raise even
in silence.
Word
Meaning
Sound(freq, ms)
Fire-and-forget blip, anonymous voice pool (pool full: oldest stolen, silently).
NoteOn(ch, freq)
Hold freq on channel ch until NoteOff. On a sounding channel: retune in place, no re-attack.
NoteOff(ch)
Release the held note (nothing held: no-op).
SoundQueue(ch, freq, ms)
Append to ch's queue; notes play back-to-back, sample-accurate. freq 0 = rest. >5 min queued ahead aborts.
SoundQueued(ch)
→ ms already queued ahead on ch, 0 once drained. The only way to see that cap coming — measure your score, add this, and refuse before queueing rather than after.
SoundStop(ch)
Silence now: release held note, flush queue.
SoundGain(ch, vol)
Channel volume 0–1, sticky until set again.
SoundWave(ch, wave)
Channel timbre, sticky: 0 square (default), 1 triangle, 2 sine. Re-voices a held note in place; the Sound pool is square only.
Notes and music — MidiFreq, NoteFreq, MmlNotes,
Play(ch, mml): buzzer.shoddy.
The machines — standard library map
Bring a machine in with
Include "machines/NAME.shoddy" (or compile it once with
mill machine). This is the map; each page carries the machine’s
full word reference, a user’s guide, and examples — start at the
machines index.
The binary layer — number bases, bit operations, masks and fields, two's complement, the codes, the logic gates, truth tables and Quine–McCluskey minimisation. Shoddy has no bitwise builtins; this is why you need none.
Engineering mathematics — trigonometry and hyperbolics, complex numbers, numeric calculus and root-finding, polynomials, number theory, the discrete distributions, units and physical constants.
The sky as arithmetic — sidereal time, altitude and azimuth for any observer, the sun and moon as sky positions, moonrise and phase, and the five naked-eye planets.
The Earth as arithmetic — great-circle distance and bearing, courses, cross-track and crossings, coordinate formats, horizon distance, and the sun's position, rise and set for any point. Pure and sphere-based.
The calendar as arithmetic — the one place the Gregorian leap rules are spelled: dates, serials, the astronomical Julian day, date arithmetic and weekdays.
Regular expressions as a compiled value — a Pike VM. It never backtracks because backreferences and lookaround are left out, so no input can hang it and none can end the session.
Querying a sequence — ordering by a key, grouping, joining, the set words, and a walker for a record graph that holds itself. The half of a query seq and stats could not express.
Symbolic algebra over an expression type — exact rationals, a parser and printer, differentiation, factorisation, integration, limits and series, and a bridge that turns a formula into a function value.
Matrices that are mostly zeros — stored by column, a row index and a value per entry and nothing for the gaps. The working form for big thin problems; matrix stays the everyday grid.
Feed-forward neural networks — one hidden Tanh layer, mini-batch SGD with momentum (learning by many small corrections), gradients as net-shaped values. Two heads over one backward pass: regression on squared error, classification on softmax (scores turned into probabilities) and cross-entropy.
CSV to RFC 4180, both directions — quoted cells, doubled quotes, line breaks inside a cell, and dialects for tabs, semicolons and comment lines. Rows, or a sheet with named columns, or straight into your own Type.
Integer programming by branch and bound over simplex — for the answers that have to be whole numbers, with honest sensitivity once the decisions are taken.
The language's own control-flow and execution-failure words — Call, Ifte, Error and Assert. Documentation only: it declares nothing, because all four are builtins.