| name | subjective |
| description | First-person code — the "I" shifts based on who is speaking |
| allowed-tools | ["read_file","write_file"] |
| tier | 1 |
| protocol | SUBJECTIVE-ORIENTED |
| tags | ["moollm","programming","perspective","first-person","context"] |
| related | ["context","runtime","object","character"] |
| adversary | third-person-coupling |
Subjective-Oriented Programming
"i_have('key') reads naturally. 'Does the world have a key?' does not."
— The Gezelligheid Grotto Design Principles
What Is It?
Subjective-oriented programming uses i_ prefixed functions that speak from the current subject's perspective. The "I" shifts based on context:
| Context | Who is "I"? |
|---|
| Player action | The player character |
| Object simulate() | The object |
| NPC dialogue | The NPC |
| Room description | The room |
The Problem with world.
if (world.has("key")) ...
The Solution: i_
if (i_have("key")) ...
Core i_ Functions
Inventory
i_have("key")
i_give("key")
i_take("key")
i_drop("key")
i_has_tag("weapon")
i_find_by_tag("food")
State
i_am("lit")
i_am_not("broken")
i_set("lit", true)
i_get("fuel")
Buffs
i_have_buff("strength")
i_add_buff(buff)
i_remove_buff("strength")
Flags
i_flag("door_unlocked")
i_set_flag("door_unlocked")
Location
i_am_in("pub/")
i_can_see("treasure")
i_go("north")
Communication
i_say("Hello!")
i_emote("grins widely")
i_think("This is odd...")
Context Shifting
The meaning of i_ changes based on who is "speaking":
Player Context
effect: |
if i_have("key"):
i_say("I found the key!")
i_drop("key") # Drop in current room
Object Context (simulate)
simulate: |
if i_am("lit"):
i_set("fuel", i_get("fuel") - 1)
if i_get("fuel") <= 0:
i_set("lit", false)
i_emit("The lamp dies!")
NPC Context
behavior: |
if i_have("gold") and i_can_see("beggar"):
i_give("gold")
i_say("For you, friend.")
Room Context
describe: |
if i_am("dark"):
i_emit("You can't see anything.")
else:
i_emit("A cozy room with a fireplace.")
Implementation
The i_ functions are bound to the current subject:
class World {
get subject() {
return this.object || this.npc || this.player;
}
i_have(itemId) {
const inv = this.subject.inventory || [];
return inv.includes(itemId);
}
i_give(itemId) {
const inv = this.subject.inventory || [];
const idx = inv.indexOf(itemId);
if (idx > -1) {
inv.splice(idx, 1);
(this.target.inventory ??= []).push(itemId);
}
}
i_take(itemId) {
const targetInv = this.target.inventory || [];
const idx = targetInv.indexOf(itemId);
if (idx > -1) {
targetInv.splice(idx, 1);
(.. ??= []).(itemId);
}
}
() {
inv = .. || [];
idx = inv.(itemId);
(idx > -) {
inv.(idx, );
(.. ??= []).({ : itemId });
}
}
() {
inv = .. || [];
( itemId inv) {
item = .(itemId);
(item && (item. || []).(tag)) {
;
}
}
;
}
() {
..?.[stateKey] === ;
}
() {
!.(stateKey);
}
() {
..?.[stateKey];
}
() {
.. ??= {};
..[stateKey] = value;
}
() {
buffs = .. || [];
buffs.( b. === buffId);
}
() {
.. ??= [];
...(buff);
}
() {
buffs = .. || [];
.. = buffs.( b. !== buffId);
}
() {
.. === roomPath ||
.?. === roomPath;
}
() {
objects = .?. || [];
objects.( o. === objectId);
}
() {
. = direction;
}
() {
.();
}
() {
.();
}
() {
.();
}
() {
.(message);
}
}
Python Equivalent
class World:
@property
def subject(self):
"""The current 'I' — object, NPC, or player."""
return self.object or self.npc or self.player
def i_have(self, item_id: str) -> bool:
inv = self.subject.get('inventory', [])
return item_id in inv
def i_am(self, state_key: str) -> bool:
return self.subject.get('state', {}).get(state_key) == True
def i_get(self, state_key: str):
return self.subject.get('state', {}).get(state_key)
def i_set(self, state_key: str, value):
state = self.subject.setdefault('state', {})
state[state_key] = value
def i_say(self, message: str):
.emit()
():
.emit(message)
Natural Language Compilation
The LLM compiles natural language to i_ calls:
guard: "I have the key and I am not cursed"
guard_js: (world) => world.i_have("key") && !world.i_have_buff("cursed")
guard_py: lambda world: world.i_have("key") and not world.i_have_buff("cursed")
simulate: |
if I am lit and fuel > 0:
consume 1 fuel
if fuel reaches 0:
I am no longer lit
say "The lamp dies!"
simulate_js: (world) => {
if (world.i_am("lit") && world.i_get("fuel") > 0) {
world.i_set("fuel", world.i_get("fuel") - 1);
if (world.i_get("fuel") <= 0) {
world.i_set("lit", false);
world.i_emit("The lamp dies!");
}
}
}
The i_ vs world. Split
Use i_ when... | Use world. when... |
|---|
| Acting as current subject | Accessing global state |
| Checking own inventory | Checking adventure flags |
| Modifying own state | Navigating rooms |
| Speaking in first person | Managing party |
if (i_have("key") && world.flag("door_visible")) {
i_say("I can unlock this door!");
world.set_flag("door_unlocked", true);
}
Benefits
- Readable —
i_have("key") reads like English
- Context-aware — "I" shifts naturally
- Object-oriented — Objects think for themselves
- Compilable — LLM can translate natural language easily
- Debuggable — Clear who is speaking in logs
Protocol Symbol
SUBJECTIVE-ORIENTED — The "I" shifts based on who is speaking