Subjective-Oriented Programming
SkillDev toolsFirst-person code — the "I" shifts based on who is speaking
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Subjective-Oriented Programming skill
What this skill tells your AI
The instructions your AI receives, as published by simhacker/moollm in skills/subjective/SKILL.md and read by ahel’s review.
"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.
// Third-person: "Does the world have a key?"
if (world.has("key")) ...
// But who has the key? The world? The player? Confusing!
The Solution: i_
// First-person: "Do I have a key?"
if (i_have("key")) ...
// Clear! The current subject is asking about themselves.
Core i_ Functions
Inventory
i_have("key") // Do I have this item?
i_give("key") // Give item to current target
i_take("key") // Take item from current target
i_drop("key") // Drop item in current room
i_has_tag("weapon") // Do I have any item with this tag?
i_find_by_tag("food") // Find all my items with this tag
State
i_am("lit") // Am I in this state? (object.state.lit)
i_am_not("broken") // Am I NOT in this state?
i_set("lit", true) // Set my state
i_get("fuel") // Get my state value
Buffs
i_have_buff("strength") // Do I have this buff?
i_add_buff(buff) // Apply buff to myself
i_remove_buff("strength") // Remove buff from myself
Flags
i_flag("door_unlocked") // Is this flag set?
i_set_flag("door_unlocked") // Set this flag
Location
i_am_in("pub/") // Am I in this room?
i_can_see("treasure") // Can I see this object?
i_go("north") // Move in direction
Communication
i_say("Hello!") // Emit dialogue
i_emote("grins widely") // Emit emote
i_think("This is odd...") // Emit internal thought
Context Shifting
The meaning of i_ changes based on who is "speaking":
Player Context
# In player action handlers
effect: |
if i_have("key"):
i_say("I found the key!")
i_drop("key") # Drop in current room
Object Context (simulate)
# In object's simulate field
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
# In NPC dialogue/behavior
behavior: |
if i_have("gold") and i_can_see("beggar"):
i_give("gold")
i_say("For you, friend.")
Room Context
# In room description/behavior
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 {
// The current subject ("I")
get subject() {
return this.object || this.npc || this.player;
}
// === INVENTORY (subjective) ===
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);
(this.subject.inventory ??= []).push(itemId);
}
}
i_drop(itemId) {
const inv = this.subject.inventory || [];
const idx = inv.indexOf(itemId);
if (idx > -1) {
inv.splice(idx, 1);
(this.room.objects ??= []).push({ id: itemId });
}
}
i_has_tag(tag) {
const inv = this.subject.inventory || [];
for (const itemId of inv) {
const item = this.getObject(itemId);
if (item && (item.tags || []).includes(tag)) {
return true;
}
}
return false;
}
// === STATE (subjective) ===
i_am(stateKey) {
return this.subject.state?.[stateKey] === true;
}
i_am_not(stateKey) {
return !this.i_am(stateKey);
}
i_get(stateKey) {
return this.subject.state?.[stateKey];
}
i_set(stateKey, value) {
this.subject.state ??= {};
this.subject.state[stateKey] = value;
}
// === BUFFS (subjective) ===
i_have_buff(buffId) {
const buffs = this.subject.buffs || [];
return buffs.some(b => b.id === buffId);
}
i_add_buff(buff) {
this.subject.buffs ??= [];
this.subject.buffs.push(buff);
}
i_remove_buff(buffId) {
const buffs = this.subject.buffs || [];
this.subject.buffs = buffs.filter(b => b.id !== buffId);
}
// === LOCATION (subjective) ===
i_am_in(roomPath) {
return this.subject.location === roomPath ||
this.room?.path === roomPath;
}
i_can_see(objectId) {
const objects = this.room?.objects || [];
return objects.some(o => o.id === objectId);
}
i_go(direction) {
this._pendingNavigation = direction;
}
// === COMMUNICATION (subjective) ===
i_say(message) {
this.emit(`${this.subject.name}: "${message}"`);
}
i_emote(action) {
this.emit(`${this.subject.name} ${action}`);
}
i_think(thought) {
this.emit(`(${this.subject.name} thinks: "${thought}")`);
}
i_emit(message) {
this.emit(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):
self.emit(f"{self.subject.get('name')}: \"{message}\"")
def i_emit(self, message: str):
self.emit(message)
# ... etc
Natural Language Compilation
The LLM compiles natural language to i_ calls:
# Natural language
guard: "I have the key and I am not cursed"
# Compiled
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")
# Natural language (object context)
simulate: |
if I am lit and fuel > 0:
consume 1 fuel
if fuel reaches 0:
I am no longer lit
say "The lamp dies!"
# Compiled
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 |
// Mixed example
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
Signals
- GitHub stars
- 52
- Forks
- 5
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
subjective- Source
- github.com/simhacker/moollm