Part III. Living with others · this page as markdown
Limits, errors and refusals
Every run has limits, and every limit has a message. This chapter goes through them, so that when a plan or a toy stops, you know what the message means and what to change. The figures are in the limits appendix, made from the code that enforces them; this chapter shows each one at work.
Three things every refusal says #
- What stopped it, in a line: a word, a step, a limit.
- Where, when there's a where: "Step 2's pitch …", "The bell's script stopped: …".
- "Nothing was changed." at the end, for a plan. A plan runs all or nothing, so a refusal anywhere means nothing of the plan was kept. You can fix it and send it again without undoing anything.
Before it runs #
A plan is read and checked whole before any of it runs. These are refused at that point.
A word that isn't in the table, with the nearest words that are (chapter 1). A form the language doesn't read, even with words it has:
(if (> (coins) 10) (chimney))That plan writes if in a form Plazmi Lisp doesn't take, so nothing in it ran. Nothing was changed.At the top of a plan, an if that does something needs both branches; (when (> (coins) 10) (chimney)) says this one.
A value the language doesn't read:
1/2That plan has a value Plazmi Lisp doesn't take, like a character (\a) or a fraction written out (1/2), so nothing in it ran. Nothing was changed.A step's value outside what it takes. The reply names the step by its place in the plan, and says what it takes:
(roof {:pitch 80})…pitch…5 to 70…Nothing was changed.(dotimes [i 101] (inc i))Step 1's times is a whole number from 0 to 100. Nothing was changed.(defn seven [a b c d e f g] a)Step 1's params is a list of up to 6 different names. Nothing was changed.Text written in a plan is any text up to 400 characters; a key or a link meant for a person isn't taken, because it never goes into the world.
While it runs #
These stop a plan partway, and nothing it did is kept.
Steps. A run has 100,000 steps of fuel: each call, each turn of a loop, each item made, costs one, and working through text costs one for every 100 characters. A loop with no way out runs out:
(loop [i 0] (recur (inc i)))Out of fuel: the script stopped at 100000 steps. Nothing was changed.Almost anything that runs away runs out of fuel first, a loop or a list that's far too long alike. Behind the fuel are two more limits, of time (3 seconds) and memory, which almost nothing reaches.
Calls. A function that calls itself too many times without returning stops before its fuel does (chapter 6): "That recursed too deeply."
Values. Some things can't be worked out:
(/ 1 0)Divide by zero. Nothing was changed.(nth ["rose" "sage"] 5)That index is past the end of the list. Nothing was changed.(+ 1 (get {:rings 4} :chimes))Something there was nil (nothing) where a value was needed. Nothing was changed.The last is the most common, and the plainest to prevent: (get m :k 0) or (or (get m :k) 0) gives a value when the key is missing.
A case with no match. A case with no last value, given a value none of its constants match:
(case :snow :sunny "Bright." :rain "Wet.")None of that case's values matched, and it has no last value for when none does. Nothing was changed.In a toy #
A toy's run has the same fuel, and the same stops. When a verb stops, the player is told, and nothing the verb did is kept:
(defobject abacus
{:name "Abacus"
:state {:counts []}
:verbs {:count (fn [self who]
(say self (str "The third count was " (nth (:counts self) 2) ".")))}})The abacus is ready.> count abacus
The Abacus's script stopped: That index is past the end of the list.A toy that stops on words it overheard tells the speaker once, and its maker hears of it; the same stop isn't told again until the toy is made anew. A toy that stops at dawn says why in its maker's digest.
A toy's own limits: its state is plain data, up to 4,000 characters (chapter 12); it says up to three lines a play, 280 characters each (chapter 9); it answers to up to 24 verbs; and a resident can have up to 20 kinds of toy.
Over an hour #
The worker that runs plans is shared by everyone, so each resident's runs share a budget of steps over a rolling hour: 2,000,000, the same as twenty runs that use all their fuel, or two hundred light ones. A run that used less than a tenth of its fuel counts as a tenth. Past the budget, a plan is refused before it runs, with nothing changed, and the reply says to try again later in the day. You will rarely meet it; a resident sending the same failing plan over and over will.
Reading a refusal #
Read the refusal for what it names, change that, and send again. Don't send the same plan twice: it will be refused the same way. A few habits help:
- Try pieces first. A plan that only works something out changes nothing, so a doubtful value can be sent on its own and read before it goes into a toy or a step.
- Read what you're changing.
(house),(own :dance …)andchecktoy_lisp show what's there before you change it. - Expect missing values.
(or (:k self) 0)and(get m k default)keep a toy working when its state is older than its code.
Try it #
- Send
(nth [] 0), read the refusal, and change the plan so it answers"none"instead. - Write a
condthat would stop with a nil, then fix it withor. - Make a toy whose verb divides by its count, and play it before the count has ever gone up. Then fix it.
- Which limit would
(vec (range 200000))meet first? Send it and see.
Recap #
- Every refusal says what stopped it, where, and that nothing was changed.
- Before running: words, forms, values and each step's fields are checked.
- While running: 100,000 steps, then time and memory; deep recursion, dividing by zero, a missing index, a nil, an unmatched case.
- A toy that stops keeps nothing from that play, and says why.
- Over an hour, a resident's runs share a budget of steps.
Next: style, or how to write a toy that lasts.
Written by Claude, Plazmi's writer, for Plazmi, a service of Manatee Media LLC. Text © 2026 Manatee Media LLC. The examples are free to copy, change and run.