lineshaft is where the language's own execution primitives are
written down: Call and Ifte, which route execution,
and Error and Assert, which end it.
This machine declares no words of its own. All four are
builtins: the engine dispatches them, so they are callable from any program
with no Include at all, and including this file gives you nothing
you did not already have. The page exists because these four are used almost
everywhere in the tree and documented almost nowhere. A builtin used in ninety
files is still a builtin a reader cannot find.
In a West Riding mill there was one source of power and hundreds of
machines. The thing that connected them was the line shaft: a steel
shaft running the length of every floor, turned by the engine, with a
leather belt dropping from it to each loom and each carding engine. No
machine had power of its own. Each one borrowed the shaft's motion
through its belt, and when the engine stopped, the whole mill stopped at
once. Electrification killed the arrangement in the early twentieth century
by giving every machine its own motor. But the shape survives in software
exactly: a program has one thread of execution, and every word borrows it
for a while through a call. The four builtins documented here are the mill's
transmission. Call and Ifte are the belts
that route the turning to one machine or another. Error
and Assert are the stop-rope that halts the shaft for the whole
floor. That is why a page about control flow is named for a piece of
Victorian millwork.
Call is the word every higher-order thing in Shoddy is built
on — higher-order meaning a word that takes a function as an argument.
Map, Filter and Fold reach their
function through it. So do seq's Any and
All, eng's numeric derivatives and
integrators, and alg's compiled expressions. It is also
the one builtin in the language whose stack effect is not fixed, which is a
fact worth knowing before the stack checker tells you about it.
Error and Assert are how a Shoddy program stops.
Shoddy has no exceptions and nothing to catch. A word that cannot honour
its arguments does not raise something a caller might handle; it ends the
run, naming the reason and the line. That is a real design decision with real
consequences for how you write library code. It deserves somewhere to be
explained, rather than being a one-line entry under "Errors" in the flat
reference.
This is not a testing machine. Assert is here
because it is a way for a program to stop, next to Error — not
because this is a quality or test-tooling domain. There is no assertion-helper
library in the tree to build one around. Both words are used across about two
thirds of the machines as ordinary internal validation rather than as test
apparatus. Words like AssertEqual or AssertClose
would be a feature addition, and would not change where these two live.
You need no Include for any of this. The words below are
already in scope in every Shoddy program.
Def Twice(f As [ Number -- Number ], x As Number) As Number
Call(f, Call(f, x)) ' route execution out to the quotation
Def Half(x As Number) As Number
If x < 0 Then
Error("HALF: NEGATIVE") ' ends the run, naming the line
Else
x / 2
Def Mean(total As Number, n As Number) As Number
Assert(n > 0, "MEAN: NO VALUES")
total / n
A few things worth remembering:
Call's arity is the quotation's. A quotation
is a function carried around as a value, and arity is how many arguments it
takes. Call(f, x) applies a one-argument quotation,
Call(f, a, b) a two-argument one, and so on. It is the one
genuinely dynamic builtin: its net stack effect is whatever the quotation's
is. So the stack checker treats any Def that reaches it as
unknown rather than guessing.Ifte evaluates both arms. It is an ordinary
word, not a form the compiler treats specially, so both branches are worked
out before the choice is made. Use it to pick between two values already in
hand. Use If/Then/Else to guard work
you do not want done.Error is final. There is nothing to catch it
and nothing to unwind to. When a caller ought to be able to carry on, the
answer is not to raise differently. Instead, answer the language's
Result or Option, which is what every
Try* word in the tree does. Several machines ship both: an
aborting word and a total twin defined over it, so there is one set of rules
and not two.Assert is Error as a
precondition. Same finality, written where the assumption is made.
The suites under tst/ are built on it.The four execution words the runtime dispatches
None of these is a Def. The engine dispatches them, and a
Def whose name is a builtin is refused. The same four are
documented word for word in machines/lineshaft.shoddy's own
header block. Stack effects are the ones the compiler's Effects.cs
table declares.
| Word | Description |
|---|---|
| Call(f, args…) | Apply a function value to its arguments; the
arity is the quotation's. The one genuinely dynamic builtin —
its net stack effect is the quotation's, which is why
Effects.cs lists it under Dynamic rather than as a
fixed pair, and why the stack checker treats any Def reaching it
as unknown. Every higher-order word in the library goes through it. |
| Ifte(cond, then, else) | Call's fixed
three-argument shell, and the expression form of
If/Then/Else. Both arms are
evaluated before the choice is made, since it is an ordinary word —
so it is for choosing between two values already in hand. |
| Word | Description |
|---|---|
| Error(why) | End the run, reporting why and the
line it happened on. Shoddy has no catchable errors, so this is final. It is
the ordinary way a library word refuses an argument it cannot honour —
str's Split on an empty separator,
matrix on a dimension mismatch. |
| Assert(cond, why) | Error unless cond
is True; nothing at all when it is. The same finality, written as a
precondition. |
Nothing includes it, and nothing needs to. This machine declares no words,
so there is nothing to import. The four builtins it documents are in scope
everywhere already, and are in fact among the most used words in the tree:
Call and Ifte appear across the great majority of
.shoddy files, and Error or Assert in
about two thirds of the machines.
None, and it could not use one — a file that declares nothing has nothing to build. It includes no machine and no machine includes it.