Goblin Object Matrices
Object matrices are Goblin's bulk object-construction syntax for simulations, games, balancing systems, RPG stat blocks, procedural content, and large sets of related structured data.
Instead of repeatedly building related objects one at a time:
usa | Entity("USA", 100, 60, 50, 70) france | Entity("France", 70, 40, 70, 50) russia | Entity("Russia", 90, 65, 50, 80)
usa | Entity("USA", 100, 60, 50, 70) france | Entity("France", 70, 40, 70, 50) russia | Entity("Russia", 90, 65, 50, 80)
Goblin lets you define the same objects side-by-side in a readable matrix:
<>Entity matrix id: "{id}", usa, france, russia power: 100, nc, 70, 90 aggression: 50, 60, 40, 65 cooperation: 50, nc, 70, nc opportunism: 50, 70, nc, 80 end
<>Entity matrix id: "{id}", usa, france, russia power: 100, nc, 70, 90 aggression: 50, 60, 40, 65 cooperation: 50, nc, 70, nc opportunism: 50, 70, nc, 80 end
This keeps fields vertical, objects horizontal, defaults centralized, and comparisons easy.
Object matrices complement normal object construction. They do not replace it.
Basic Syntax
A matrix starts with a class name and the matrix keyword:
<>Entity matrix id: "{id}", usa, france, russia power: 100, nc, 70, 90 aggression: 50, 60, 40, 65 cooperation: 50, nc, 70, nc opportunism: 50, 70, nc, 80 resilience: 50, 80, 65, 75 influence: 50, 90, 70, 85 stability: 50, 65, 80, 55 alive: true, nc, nc, nc end
<>Entity matrix id: "{id}", usa, france, russia power: 100, nc, 70, 90 aggression: 50, 60, 40, 65 cooperation: 50, nc, 70, nc opportunism: 50, 70, nc, 80 resilience: 50, 80, 65, 75 influence: 50, 90, 70, 85 stability: 50, 65, 80, 55 alive: true, nc, nc, nc end
The matrix is self-contained. It defines the schema, sets defaults, and creates all instances in one block.
If Entity already exists, the matrix uses that class. If it does not exist yet, Goblin infers the class schema from the matrix rows.
Matrix Rows
Each row follows this structure:
field_name: default_value, object1, object2, object3
field_name: default_value, object1, object2, object3
The first value after : is the default.
Each remaining value corresponds to an object column.
aggression: 50, 60, 40, 65
aggression: 50, 60, 40, 65
This means:
usa >> aggression /// 60 france >> aggression /// 40 russia >> aggression /// 65
usa >> aggression /// 60 france >> aggression /// 40 russia >> aggression /// 65
No Change Values
Use nc or :: when an object should inherit the row default.
cooperation: 50, nc, 70, nc
cooperation: 50, nc, 70, nc
This means:
usa >> cooperation /// 50 france >> cooperation /// 70 russia >> cooperation /// 50
usa >> cooperation /// 50 france >> cooperation /// 70 russia >> cooperation /// 50
nc reads as "no change." It says: use the default for this field.
The ID Row
The id row is special.
id: "{id}", usa, france, russia
id: "{id}", usa, france, russia
The column names determine the objects created by the matrix:
usa france russia
usa france russia
The "{id}" placeholder means: use each column name as the value for that object's id field.
So usa receives:
usa >> id /// "usa"
usa >> id /// "usa"
and france receives:
france >> id /// "france"
france >> id /// "france"
Equivalent Expansion
This matrix:
<>Entity matrix id: "{id}", usa, france power: 100, nc, 70 aggression: 50, 60, 40 end
<>Entity matrix id: "{id}", usa, france power: 100, nc, 70 aggression: 50, 60, 40 end
is equivalent to defining the class shape and constructing the objects separately:
<>Entity id: "" power: 100 aggression: 50 end usa | Entity id: "usa" power: 100 aggression: 60 end france | Entity id: "france" power: 70 aggression: 40 end
<>Entity id: "" power: 100 aggression: 50 end usa | Entity id: "usa" power: 100 aggression: 60 end france | Entity id: "france" power: 70 aggression: 40 end
The matrix simply does both jobs in one readable block.
Expressions in Matrices
Matrix cells can contain pure expressions.
power: 100, 70 + 5, nc, 90 * 1.1
power: 100, 70 + 5, nc, 90 * 1.1
This turns matrices into balancing tools rather than static data dumps.
Field References
A matrix cell can reference fields declared above it in the same object column.
<>Character matrix id: "{id}", warrior, rogue, wizard strength: 10, 18, 12, 8 dexterity: 10, 8, 18, 12 threat: 0, strength + dexterity, strength + dexterity, strength + dexterity end
<>Character matrix id: "{id}", warrior, rogue, wizard strength: 10, 18, 12, 8 dexterity: 10, 8, 18, 12 threat: 0, strength + dexterity, strength + dexterity, strength + dexterity end
Results:
warrior >> threat /// 26 rogue >> threat /// 30 wizard >> threat /// 20
warrior >> threat /// 26 rogue >> threat /// 30 wizard >> threat /// 20
Field references only look upward. A field can reference fields declared before it, not fields declared later.
Default References
Use default to reference the row's default value.
power: 100, default + 10, 70, default - 5
power: 100, default + 10, 70, default - 5
This produces:
object1 >> power /// 110 object2 >> power /// 70 object3 >> power /// 95
object1 >> power /// 110 object2 >> power /// 70 object3 >> power /// 95
Dice Notation
Object matrices support Goblin's native dice notation.
This makes matrices useful for RPG systems, enemy generation, procedural stats, loot tables, and simulations.
<>Character matrix id: "{id}", rogue, wizard, barbarian strength: 10, 1d8+2, 1d4+1, 2d8 dexterity: 10, 2d10, 1d6, 1d8 wisdom: 10, 1d20+1, 2d10+2, 1d6 end
<>Character matrix id: "{id}", rogue, wizard, barbarian strength: 10, 1d8+2, 1d4+1, 2d8 dexterity: 10, 2d10, 1d6, 1d8 wisdom: 10, 1d20+1, 2d10+2, 1d6 end
Dice expressions in ordinary stat fields are evaluated once when the object is created.
Example results:
rogue >> strength /// 7 wizard >> wisdom /// 16
rogue >> strength /// 7 wizard >> wisdom /// 16
Persistent Roll Expressions
Some systems need the dice expression itself to stay dynamic.
For example, a weapon's damage roll should usually be rolled later during gameplay, not once when the weapon is created.
<>Weapon matrix id: "{id}", longsword, dagger, warhammer attack_roll: 1d4, 1d8+2, 1d4+1, 2d6+3 end
<>Weapon matrix id: "{id}", longsword, dagger, warhammer attack_roll: 1d4, 1d8+2, 1d4+1, 2d6+3 end
The expression can be stored and rolled later:
damage | hero >> weapon >> attack_roll >> roll
damage | hero >> weapon >> attack_roll >> roll
Matrix Evaluation Rules
Allowed inside matrix cells:
- literals
nc/::- pure expressions
- upward field references
- dice notation
Not allowed:
- side effects
- arbitrary actions
- mutation logic
- execution blocks
power: 100, default + 10 /// valid threat: 0, power + aggression /// valid strength: 10, 1d8+2 /// valid power: 100, attack(enemy) /// invalid name: "{id}", save_file() /// invalid
power: 100, default + 10 /// valid threat: 0, power + aggression /// valid strength: 10, 1d8+2 /// valid power: 100, attack(enemy) /// invalid name: "{id}", save_file() /// invalid
Matrices are for structured object construction, not arbitrary execution.
Simulation Use
In simulations, matrices define seed state.
<>Entity matrix id: "{id}", kingdom_a, kingdom_b, kingdom_c aggression: 50, 70, 40, 80 cooperation: 50, 20, 90, 10 stability: 50, 40, 80, 30 famine_risk: 10, 20, 50, 70 end
<>Entity matrix id: "{id}", kingdom_a, kingdom_b, kingdom_c aggression: 50, 70, 40, 80 cooperation: 50, 20, 90, 10 stability: 50, 40, 80, 30 famine_risk: 10, 20, 50, 70 end
Simulation logic can then mutate runtime copies over time:
if famine_risk > stability unrest | unrest + 20 end
if famine_risk > stability unrest | unrest + 20 end
A simulation log might later show:
Round 12: russia power: 90 -> 96 france stability: 80 -> 63 usa influence: 90 -> 97
Round 12: russia power: 90 -> 96 france stability: 80 -> 63 usa influence: 90 -> 97
The matrix is the starting board, not permanent truth.
Intended Use Cases
Object matrices are ideal for:
- simulation entities and factions
- RPG stats, enemies, and NPC archetypes
- weapons, armor, and loot tables
- balancing systems
- procedural seeds
- test fixtures
- configuration profiles
Why Object Matrices Exist
Positional constructors become unreadable after enough fields.
usa | Entity("usa", 100, 60, 50, 70, 80, 90, 65)
usa | Entity("usa", 100, 60, 50, 70, 80, 90, 65)
After enough values, you forget what each position means.
Object matrices keep the field names visible and the objects comparable.
Most languages force this kind of data into giant JSON blobs, unreadable constructors, ECS boilerplate, or spreadsheets disconnected from code. Goblin keeps structure, tuning, generation, and simulation inside one coherent syntax.
Design Philosophy
Object matrices are designed around readability, balancing clarity, and side-by-side comparison.
They are especially suited for emergent simulations, RPG systems, dynamic world modeling, and systemic game design.
Rather than treating data as disconnected objects, matrices let you think in terms of systems, relationships, balance, and evolving state.
If you can visually compare objects side-by-side, balancing and systemic tuning become dramatically easier.