In this post, attempting to sketch out a syntax. If this isn't on-topic
enough for cap-talk let me know... I think it ties in for petname and
ocap UI exploration reasons but am happy to take it off of here if it's
not considered relevant enough to others. However, I think a textual
world style game is just rich enough but also just simplified enough UI
wise to think through some interesting interactions clearly.
Many ideas are drawn from the recent Petnames and Secure UIs writeups
from Rebooting Web of Trust:
It's no surprise that there's a correlation between petnames and lexical
scope; Mark called his article "Lambda for Humans":
Likewise, at a REPL, a programmer is also operating within a scope, and
programmers have tended to understand what objects they're operating
Rough syntax for the different kinds on names in a petname system:
- @bob : petname for bob
- ?cat : self-proclaimed name for a cat in the current "context"
- @bob>carol : edge name bob provides for carol
As such, we might see the following room description:
You are in a ?forest. There is a ?fountain in the middle of
the ?clearing. There are some ?benches here to relax on.
@Smithy is sitting on a ?bench.
@Alice is sitting on a ?bench+1.
@Carol, ?Spartacus, ?Spartacus+1, and ?Spartacus+2 are hanging out.
Here we see that:
- @Smithy, @Alice, and @Carol are all petnames, indicated by the
preceding `@` (which is not part of the petname but is syntax to
identify it as such and allow us to address it). `@` is chosen as
prefix because Twitter has popularized that usage (even though
petnames have better integrity promises than Twitter gives).
- We see here some self-proclaimed names... ?forest, ?fountain,
?clearing, and ?benches are all self-proclaimed names within this
room-context (more on room-contexts in a moment). `?` is chosen as a
prefix exactly because it does not have popular usage, and it looks
immediately "question-able". It does not mean that the name is not
useful, it is a name supplied within a context and may carry some
useful information... but that context does not have the level of
trust we would like.
- We also see some self-proclaimed names with collisions. We have the
NPCs of ?bench, but there is a second bench, ?bench+1 (note that
these are distinct from ?benches, more on that later). Likewise,
we have several users all proclaiming the same name:
?Spartacus, ?Spartacus+1, and ?Spartacus+2.
There is one more naming syntax, though I do not think it will appear in
game contexts (but may in other social network contexts): `@Bob>Carol`
indicates that this is Bob's edge name they describe as Carol. Edge
names are highly desirable but have some tricky edges around privacy,
and are not the focus of this as much as they were in the Secure UIs
Mastodon/Twitter-like social network example. I will come around
to discussing them, as well as "sub-profiles for multiple worlds" in a
followup post, since both introduce some unresolved questions.
Now that we have our syntax to referring to objects, and we can see some
descriptive output, how do we enter input? (To avoid ambituity with
quoting in followup posts I will make the prompt look like `GAME>`)
Here are a few simple commands:
;; Look around, generally
;; Look at the (self-proclaimed) fountain.
;; The first argument is the "direct object" we'd like to interact
;; with, though methods are allowed whatever arity they'd like with
;; whatever meaning for each argument position they'd like.
GAME> look ?fountain
;; Look at the entity which we have petname'd as Smithy
GAME> look @Smithy
;; Place book into a chest.
;; `in ?chest` the application of a keyword argument,
;; like (put #:in "chest") in Guile/Racket,
;; or put(in="chest") in Python
GAME> put ?book in ?chest
;; A string on its own is an implicit "say"
GAME> "Hey everyone, how's it going?"
These actually are shorthands. They expand to the following:
GAME> [.look ?fountain]
GAME> [.look @Smithy]
GAME> [.put ?book in ?chest]
GAME> [.say "Hey everyone, how's it going?"]
What's with the dot? All of these are commands that execute on our
"controller" of our player (the strings by which we generally move our
puppet)... our player's controller has special behavior associated with
each of these method names. So actually, that first one *really*
Some further expansion is available for the other commands, but before
we get to that, let's pretend we're in a room with a ?dog. We'd like to
pet the dog. Unfortunately, our player controller has no "pet" method,
so this doesn't work:
GAME> pet ?dog
ERROR: player does not have 'pet method
However, the dog *does* have a pet method, which we can invoke:
You pet ?dog on the head. It leans in and pants happily!
GAME> ?dog.pet "belly"
You rub ?dog's belly. It rolls around on the floor happily!
GAME> ?dog.pet "antenna"
It's not possible to pet "antenna" on ?dog!
Expanding these makes the previous dot syntax make more sense:
GAME> [?dog.pet "belly"]
GAME> [?dog.pet "antenna"]
Expanding on expansion
We mentioned that further expansion is available. There is a
distinction between  and (), but most users will not use ()... 
interprets game syntax, whereas () is fully expanded scheme that
runs in our client.
GAME> ((room-get "dog") 'pet)
GAME> ((room-get "dog") 'pet "belly")
GAME> ((room-get "dog") 'pet "antenna")
GAME> look @Smithy
GAME> [.put ?book in ?chest]
GAME> ((pcontrol 'look) (petname-get "Smithy"))
GAME> ((pcontrol 'put) (room-get "book") #:in (room-get "chest"))
As you can guess, petname-get fetches from our petname database, whereas
room-get fetches from the self-proclaimed names supplied within this
local "room context".
Actually... how does ?Spartacus[...] expand?
GAME> greet ?Spartacus ?Spartacus+1 ?Spartacus+3
GAME> ((pcontrol 'greet) (room-get "Spartacus" 0)
(room-get "Spartacus" 1)
(room-get "Spartacus" 2))
Thus room-get has a default second argument of 0.
Now we can think in terms of scopes. Our game prompt is clearly
basically a REPL, sugar'ified, with some useful bindings. We could
GAME> (let* ((orig-room-get room-get)
(lambda (obj-name num)
(if (equal? obj-name "dog")
(orig-room-get "horse" num)
(orig-room-get obj-name num)))))
You pet ?horse, and it neighs happily!
The above redefines within the let scope room-get to rebind ?dog to
?horse. Now why would you want to do that? I have no idea. Just
showing you the general idea here. However the inverse is possible:
(It may be that () scheme syntax has to be turned on in some "advanced"
setting to help newbies not be tricked into giving away their
GAME> say (random-phrase)
GAME> say (random-phrase) style "growl"
Could make you say or growl some random curious phrase.
One thing we can observe is that we actually have *three* namespaces (we
might actually need four, one extra for our "inventory"...). One is the
general scheme namespace, unexposed unless () is used (this is partly to
avoid a phishing attack). Within our scheme namespace, petname-get and
room-get are bound to @ and ? respectively.
Moving between rooms
Did you pick up on the clue from `room-get`? Self-proclaimed names are
contextual. Effectively, entering a new room shadows the previous REPL,
rebinding room-get to a new append-only naming context (new names are
added as new entities with no associated petname enter the room). This
also means that game descriptions matching the pre
We might be encountering goblins over and over again in our
game, and ?goblin+95152680 is not going to be meaningful to us.
However, "within" a specific room, we are likely to be able to remember
the difference between ?goblin, ?goblin+1, and ?goblin+2.
This means that if our player heads east, and all three players
self-proclaimed as "Spartacus" follow, the ordering of their names might
be different within the next room. This feels semi-undesirable, but on
the other hand hints at the right thing: that self-proclaimed names are
mostly unreliable and only can be understood within a small context.
We should not expect to remember the difference between ?Spartacus+1 and
?Spartacus+2 the following week from now.
Thus the following useful meme emerges: "If you liked it, you should
have put an @ on it." Let's say we take a liking to ?Spartacus+1 and
want to remember who they are the next time we see them... we could save
them in our petname database as @Sparty and now we can remember who our
friend @Sparty is everywhere.
Strings are quasiquotes
If we enter a room and say,
GAME> say "Hey Sparty, how's it going?"
this might not make sense to our fellow participants, who don't know who
However, if we do:
GAME> say "Hey @Sparty, how's it going?"
this can translate into "Hey ?Sparticus+1, how's it going?" for Alice,
and "Hey @Spartacular, how's it going?" for Bob, which would be
appropriate to their relevant naming contexts.
This actually means that the above phrase translates to:
GAME> (pcontrol 'say (make-qstring "Hey "
", how's it going?"))
`?` obviously also should work as such, arguably `()` and `` as well
though I'm less convinced this is worth the confusion.
- Keywords restricts the kind of syntax allowed. This could be
confusing; an alternative is to use :foo style keywords, but
since we don't *have* to type that character all the time
- Names with annoying characters:
GAME> ?"Harold the \"Mighty\"".praise
- Tab completion should be available in all the same places we expect
for any decent REPL / IDE.
Challenges not yet addressed
- What about petnames and game-world-specific sub-profiles?
- What about petnames and per-room facets vs game-world identity?
- We mentioned the possibility of having an inventory but did not give
it an example.
- What does a sale look like?
- If we teleport a user out of a room and room-get changes its context,
can we perform an attack where we switch out the `?shopkeeper` being
paid? (Do we care?)
- We do not attempt to normalize singular vs plural names. However we
might want to normalize case.