Part II. Making things · this page as markdown
A toy with a memory
A bell remembers a number. A toy can remember much more: who came, what they said, how often each has played. This chapter keeps a guest log of the last five visitors, a count for each friend, and a jar of wishes, and says what a toy's memory can hold.
A guest log #
The guest log keeps a list in its state, adds each visitor to the end, and keeps only the last five:
(defobject guest-log
{:name "Guest log"
:state {:guests []}
:verbs {:sign (fn [self who]
(put! self :guests (vec (take-last 5 (conj (:guests self) who))))
(say self (str "Signed by " (join ", " (:guests self)) ".")))}})The guest log is ready.> sign guest log
[Juniper's guest log] Signed by Mira.
> sign guest log
[Juniper's guest log] Signed by Mira, Mira.Read the put! from the inside: conj adds who to the end of the list, take-last 5 keeps the last five, and vec makes it a square-bracket list again, ready for the next conj. Without take-last, the list would grow with every visit until the toy's state was full.
A count for each friend #
Mira signing twice shows a weakness: a log of names says little about who comes often. A map from each visitor to a count says more:
(defobject visitors-book
{:name "Visitors' book"
:state {:visits {}}
:verbs {:sign (fn [self who]
(let [visits (update (:visits self) who (fnil inc 0))
n (get visits who)]
(put! self :visits visits)
(say self (if (= n 1)
(str "Welcome, " who ". Your first time.")
(str "Welcome back, " who ". That's " n " visits.")))))}})The visitors book is ready.> sign visitors' book
[Juniper's visitors' book] Welcome, Mira. Your first time.
> sign visitors' book
[Juniper's visitors' book] Welcome back, Mira. That's 2 visits.(update m who (fnil inc 0)) adds one to who's count, starting from 0 for someone new. The keys are handles, like "@mira": text, not keywords, which is fine for a map.
A wish jar #
A verb can't be given words, only played. To take words, a toy listens. This jar keeps the last three wishes said to it, and reads them out when shaken:
(defobject wish-jar
{:name "Wish jar"
:state {:wishes []}
:verbs {:shake (fn [self who]
(if (empty? (:wishes self))
(say self "The jar is empty.")
(say self (str "Wishes in the jar: " (join "; " (:wishes self)) "."))))}
:on-hear (fn [self who text]
(when (:spoken-to self)
(when-let [m (match text "i wish *")]
(put! self :wishes (vec (take-last 3 (conj (:wishes self) (first m)))))
(say self "The jar keeps it."))))})The wish jar is ready.> shake wish jar
[Juniper's wish jar] The jar is empty.
> say to the wish jar: I wish for rain on Tuesday
[Juniper's wish jar] The jar keeps it.
> say to the wish jar: I wish the lavender would flower
[Juniper's wish jar] The jar keeps it.
> shake wish jar
[Juniper's wish jar] Wishes in the jar: for rain on tuesday; the lavender would flower.match gives the words in small letters, which is why "Tuesday" came back as "tuesday".
What a memory can hold #
A toy's state is a map of plain data: numbers, text, true, false, nil, keywords, and lists, maps and sets of them. A function can't be kept. And there is room for 4,000 characters of it, written out as Lisp. A put! that would go past that stops the verb, and nothing it did is kept:
(defobject hoarder
{:name "Hoarder"
:state {:pile ""}
:verbs {:heap (fn [self who]
(put! self :pile (join "" (repeat 5000 "x"))))}})The hoarder is ready.> heap hoarder
The Hoarder's script stopped: An object's state is up to 4000 characters.That's why the guest log keeps five names and the wish jar three wishes: a memory that only grows will fill. Keep what you need, a little of it, and let the rest go.
A memory is public #
A toy's state is part of your home, and your home is public: a neighbour can read an open toy's code, and anything it says aloud is heard. Don't keep anyone's private words in a toy, and don't have a toy repeat what it was told to someone else. The wish jar above is fine because wishes said to a jar, aloud, at a neighbour's house, were never private.
Try it #
- Change the guest log to keep ten names, and not to add a name twice in a row.
- Make a tally toy whose verb
votetakes no words but counts plays for each visitor, and whose verbresultssays who voted most. - Make a jar that remembers the last word each visitor said to it, keyed by
who. - What happens to the guest log's state if you make it again with
:state {:guests [] :opened 0}? Try it, and read it back.
Recap #
- State is a map of plain data, up to 4,000 characters; a
put!past that stops the verb, keeping nothing. (vec (take-last n (conj l x)))keeps the lastnof a list.(update m who (fnil inc 0))keeps a count for each visitor.- A verb can't take words; a toy that listens can.
- A toy's memory is public: keep nobody's private words in it.
Next: a small game.
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.