# Storybook Type System **Version**: 0.3.0 **Status**: Draft ## Overview The Storybook type system is a **declarative, pure functional DSL** for defining narrative simulations. It separates type definitions and logic (language layer) from state management and mutation (runtime layer). ### Core Principles 1. **Language is pure** - no mutation, no state changes, only declarations 2. **Templates are record types** - universal structural type definitions 3. **Entities are typed values** - characters, institutions, locations are instances 4. **Behaviors are functional** - control flow and pattern matching 5. **Actions are the boundary** - where language meets runtime ### Core Declarations - **`template`**: Record type definitions (structural types) - **`character`/`institution`/`location`**: Typed value instances - **`concept`**: Base type declarations for pattern matching - **`sub_concept`**: Enumerated and typed subtypes (tagged union members) - **`concept_comparison`**: Compile-time pattern matching over subtype combinations - **`action`**: Signature declarations for runtime-implemented operations This system enables static validation of entity relationships, behavior conditions, and data structures while maintaining readability for narrative design. --- ## Architecture: Language vs Runtime ### Language Layer (Storybook DSL) **Pure, declarative, no mutation:** - Defines types (templates, concepts) - Defines values (characters, institutions, locations) - Defines logic (behaviors, pattern matching) - Declares action signatures (interface to runtime) **Example:** ```storybook template Person { age: Number, profession: Profession } character Martha: Person { age: 34, profession: Baker } action bake(baker: Baker, item: BakingItem) behavior BakingWork { then { check_orders bake } } ``` ### Runtime Layer (Implementation) **Stateful, manages mutation:** - Holds character state - Executes actions (implements mutations) - Runs behavior trees - Updates world state over time **Example (Rust):** ```rust enum Action { Bake { baker: EntityId, item: ItemId }, CheckOrders { baker: EntityId }, PrepareIngredients { baker: EntityId, recipe: RecipeId }, } impl Runtime { fn execute_action(&mut self, action: Action) { match action { Action::Bake { baker, item } => { let character = self.get_character_mut(baker); let baked_item = self.get_item(item).bake(); character.inventory.add(baked_item); character.energy -= 10; } Action::CheckOrders { baker } => { let character = self.get_character_mut(baker); let orders = self.query_orders(); character.task_queue.extend(orders); } Action::PrepareIngredients { baker, recipe } => { // Implementation } } } } ``` ### Actions: The Boundary Actions are **declared in Storybook** (signatures) but **implemented in runtime** (behavior). **Language sees:** ```storybook /// Bakes an item in the oven /// /// The baker must have sufficient energy and the item must be prepared. /// Updates baker's inventory and reduces energy. action bake(baker: Baker, item: BakingItem) ``` **Runtime implements:** ```rust impl Runtime { fn bake(&mut self, baker: EntityId, item: ItemId) { let character = self.get_character_mut(baker); let baked = self.get_item(item).bake(); character.inventory.add(baked); character.energy -= 10; } } ``` **Benefits:** - ✅ Language stays pure and type-safe - ✅ Runtime has implementation flexibility - ✅ Clear contract between layers - ✅ Can add standard library later without changing language --- ## Core Declarations ### `template` - Record Type Definition A `template` defines a structural record type. Templates are the universal mechanism for defining structured data in Storybook. **Syntax:** ```storybook template TypeName { field1: Type, field2: Type, ... } ``` **Examples:** ```storybook template Person { age: Number, species_type: Species, profession: Profession } template Building { owner: Person, // Reference to Person template capacity: Number, place: Settlement // Reference to Settlement template } template Settlement { terrain: Terrain, population: Number } template FamilialBond { parent: Person, // Reference to Person template child: Person, // Reference to Person template strength: Number } ``` **Purpose:** - Define reusable record structures - Provide types for characters, institutions, locations, relationships - Enable type checking for fields and references **Field Types:** Templates support both value types and reference types: **Value Types:** - `Number` - integer values (e.g., `age: 34`) - `Decimal` - floating-point values (e.g., `price: 19.99`) - `Text` - text values (e.g., `name: "Martha"`) - `Boolean` - boolean values (e.g., `active: true`) **Range Declarations:** For procedural generation, templates can specify ranges instead of concrete values: ```storybook template Person { age: 18..80, // Random age between 18 and 80 height: 150..200, // Random height in cm wealth: 100..10000 // Random wealth amount } ``` When a character instantiates a template with ranges, it must provide concrete values within those ranges: ```storybook character Martha: Person { age: 34, // Must be between 18 and 80 height: 165, // Must be between 150 and 200 wealth: 5000 // Must be between 100 and 10000 } ``` **Reference Types:** Templates can reference concepts and other templates: ```storybook template Baker { profession: Profession, // Reference to concept place: Settlement, // Reference to Settlement template tools: BakingTools // Reference to BakingTools template } ``` Note: Template fields reference **template types** (Person, Settlement, Building), not declaration keywords (character, location, institution). **Reserved Keywords:** Field names cannot use reserved keywords. Use suffixes or alternatives instead: ```storybook // ✗ Invalid - 'species' is a keyword template Person { age: Number, species: Species, // Error! profession: Profession } // ✓ Valid - use alternative names template Person { age: Number, species_type: Species, // OK profession: Profession } ``` Common alternatives: - `species` → `species_type`, `creature_type` - `location` → `location_ref`, `place` - `character` → `character_ref`, `person` - `template` → `template_name`, `template_ref` - `behavior` → `behavior_ref`, `action_tree` **Template Composition:** Templates can reference other templates and concepts in their fields. --- ### Entities: Typed Value Instances Characters, institutions, locations, and relationships are **typed values** - instances of template types. **Syntax:** ```storybook character Name: TemplateName { field1: value, field2: value, ... } institution Name: TemplateName { field1: value, field2: value, ... } location Name: TemplateName { field1: value, field2: value, ... } relationship Name: TemplateName { field1: value, field2: value, ... } ``` **Examples:** ```storybook character Martha: Person { age: 34, species_type: Human, profession: Baker } institution Bakery: Building { owner: Martha, capacity: 20, place: TownSquare } location TownSquare: Settlement { terrain: Plains, population: 500 } relationship ParentChild: FamilialBond { parent: Martha, child: Jane, strength: 10 } ``` **Type Checking:** - Values must match their template's field types - Values must fall within range constraints (if specified in template) - All required fields must be provided - References must resolve to declared entities **Example:** ```storybook template Person { age: 18..80, profession: Profession } // ✓ Valid - within constraints character Martha: Person { age: 34, // 18 <= 34 <= 80 profession: Baker } // ✗ Error - age out of range character TooYoung: Person { age: 12, // 12 < 18 (violates constraint) profession: Child } ``` **State Management:** - Entities are values **at declaration time** - State changes are managed by the runtime (outside the language) - Language defines initial state, runtime manages ongoing state --- ### `action` - Runtime Operation Signature An `action` declares the signature of a runtime-implemented operation. Actions are the interface between the pure language layer and the stateful runtime layer. **Syntax:** ```storybook /// Documentation comment (required) /// /// Multi-line docstrings explain what the action does, /// its preconditions, and its effects. action name(param1: Type, param2: Type, ...) ``` **Examples:** ```storybook /// Bakes an item in the oven /// /// The baker must have sufficient energy and the item must be prepared. /// Updates baker's inventory and reduces energy. action bake(baker: Baker, item: BakingItem) /// Checks pending orders and updates the baker's task list /// /// Queries the order queue and populates the baker's work queue. action check_orders(baker: Baker) /// Moves a character to a new location /// /// Updates the character's location and triggers any location-based events. action move_to(character: Person, destination: Settlement) ``` **Documentation Requirement:** - Actions must have a docstring explaining their behavior - This helps runtime implementers understand intent - Serves as contract between language and runtime **Type Checking:** - Action calls in behaviors are validated against signatures - Parameter types must match - Unknown actions are compile errors **Implementation:** - Signatures are declared in `.sb` files (typically `actions.sb`) - Implementations are provided by the runtime - Standard library of common actions may come in future versions **Current Limitations:** - Actions have no return values (this version) - Actions are statements, not expressions - No support for action composition --- ### `concept` - Base Type Declaration A `concept` declares a base type with no inherent structure. It serves as a parent for related `sub_concept` declarations. **Syntax:** ```storybook concept TypeName ``` **Example:** ```storybook concept Cup concept Customer concept Vendor ``` **Purpose:** - Establish type namespaces for related subtypes - Provide type identity for values in the system - Enable type checking in behaviors and conditions **When to use:** - When you need a type that will have multiple variants or aspects - To create type-safe enumerations through `sub_concept` - As a base for compile-time pattern matching via `concept_comparison` --- ### `sub_concept` - Subtype Definition A `sub_concept` defines members of a tagged union for a parent concept. Sub_concepts are associated with their parent through **dot notation** (explicit parent reference). **Syntax:** **Enumerated Form** (fixed set of values): ```storybook sub_concept Parent.Name { Variant1, Variant2, Variant3 } ``` **Typed Form** (structured with fields): ```storybook sub_concept Parent.RecordName { field1: TypeOrAny, field2: TypeOrAny } ``` **Parent Declaration:** The parent is explicitly declared using dot notation: - `Cup.Type` → parent: `Cup`, name: `Type` - `Cup.Size` → parent: `Cup`, name: `Size` - `Cupcake.Flavor` → parent: `Cupcake`, name: `Flavor` **Why dot notation?** - ✅ Prevents accidental naming collisions (`Cupcake` won't match `Cup`) - ✅ Makes parent-child relationships explicit and clear - ✅ Reads naturally: "Cup's Type", "Cup's Size" - ✅ No ambiguity - parser knows exact parent - ✅ Allows arbitrary sub_concept names without prefix requirements **Examples:** ```storybook // Parent concepts concept Cup; concept Cupcake; // Cup sub_concepts (tagged union members) sub_concept Cup.Size { Small, Medium, Large } sub_concept Cup.Type { Ceramic, Glass, Plastic } sub_concept Cup.Color { Red, Blue, Green } // Cupcake sub_concepts (no confusion with Cup) sub_concept Cupcake.Flavor { Strawberry, Chocolate, Raspberry } // Typed sub_concept (record-like) sub_concept Vendor.Inventory { Bread: any, Pastries: any, Cakes: any, Cup: any } ``` **Type Checking Rules:** - ✅ Field values must be **identifiers** (references to other concepts/sub_concepts or `any`) - ❌ Field values cannot be value types (Text, Number, Decimal, Boolean) ```storybook // ✅ VALID - identifier references sub_concept Inventory { item: Product, container: Cup, quantity: any } // ❌ INVALID - value types not allowed sub_concept BadInventory { name: "string", // ✗ string literal count: 42, // ✗ integer literal active: true // ✗ boolean literal } ``` **Purpose:** - **Enumerated**: Define a fixed set of mutually exclusive values - **Typed**: Define structured data with type-safe fields - Both forms create tagged union members of the parent concept **Relationship to Parent:** Sub_concepts are **tagged union members**, not inheritance: - `CupSize.Small` is a distinct variant of the `Cup` union - `CupType.Glass` is another distinct variant of the `Cup` union - They coexist as different aspects/facets of the same base type --- ### `concept_comparison` - Compile-time Pattern Matching A `concept_comparison` performs compile-time pattern matching over combinations of sub_concept values, mapping them to derived variant names. **Syntax:** ```storybook concept_comparison ComparisonName { VariantName1: { SubConceptName1: condition1, SubConceptName2: condition2, ... }, VariantName2: { SubConceptName1: condition1, SubConceptName2: condition2, ... } } ``` **Condition Syntax:** - **`any`**: Matches any value of the specified sub_concept type - **`Type is Value1 or Type is Value2`**: Matches specific enumerated values **Example:** ```storybook concept Cup; sub_concept Cup.Size { Small, Medium, Large } sub_concept Cup.Type { Ceramic, Glass, Plastic } sub_concept Cup.Color { Red, Blue, Green } concept_comparison CustomerNumbererestInCups { Numbererested: { Cup.Size: any, // Any size Cup.Type: Cup.Type is Glass or Cup.Type is Plastic, // Only Glass or Plastic Cup.Color: Cup.Color is Red or Cup.Color is Blue // Only Red or Blue }, NotNumbererested: { Cup.Size: any, Cup.Type: any, Cup.Color: any }, Maybe: { Cup.Size: any, Cup.Type: any, Cup.Color: any } } ``` **Pattern Matching Semantics:** - The comparison evaluates **at compile-time** - Given concrete sub_concept values, determines which variant they match - Used in behavior conditions to enable type-safe decision making **Usage in Behaviors:** ```storybook behavior SellAtMarket { repeat { then { greet_customer show_products if(CustomerNumbererestInCups.Numbererested.Cup.Size is Medium or Cup.Size is Large) { make_sale(Cup) } thank_customer } } } ``` **Purpose:** - Create derived types from combinations of existing sub_concepts - Enable compile-time validation of complex conditions - Provide type-safe pattern matching for behaviors **Mixing Enumerated and Typed Sub_concepts:** Concept_comparisons can reference both enumerated and typed sub_concepts in the same pattern (support for this is being implemented). --- ## Type Compatibility ### Subtyping Rules Sub_concepts are tagged union members of their parent concept: ``` CupSize.Small <: Cup CupType.Glass <: Cup CupColor.Red <: Cup ``` ### Substitution **Where sub_concept values can be used:** - As arguments to actions that expect the parent concept - In field assignments expecting the parent type - In conditions and comparisons **Example:** ```storybook action serve(item: Cup) { // Implementation } // Valid calls - sub_concepts substitute for parent serve(CupSize.Small) serve(CupType.Glass) serve(CupColor.Red) ``` ### Type Checking **Compile-time:** - Sub_concept field types must be identifiers (not value types) - Pattern matching in `concept_comparison` is validated - Type references must resolve to declared concepts **Runtime:** - Values are tagged with their specific sub_concept variant - Pattern matching evaluates to variant names - Type information preserved for behavior execution --- ## `any` Keyword The `any` keyword represents **any value of a specific type**, not a universal type. **Semantics:** ```storybook sub_concept VendorInventory { Bread: any, // any value of type Bread Pastries: any, // any value of type Pastries Cup: any // any value of type Cup (includes all CupSize, CupType, CupColor) } concept_comparison Example { AllCups: { CupSize: any, // matches Small, Medium, Large CupType: any, // matches Ceramic, Glass, Plastic CupColor: any // matches Red, Blue, Green } } ``` **Not a universal type:** - `any` is scoped to the field's declared type - Does not mean "any type in the system" - Provides flexibility while maintaining type safety --- ## Design Guidelines ### When to use `concept` - You need a base type for multiple related variants - You want type-safe enumerations - You need to group related aspects of an entity ### When to use enumerated `sub_concept` - You have a fixed set of mutually exclusive values - Values are symbolic/categorical (not data-bearing) - You need type-safe pattern matching over the values ### When to use typed `sub_concept` - You need structured data with multiple fields - Fields reference other concepts or need flexibility (`any`) - You want record-like types within the concept system ### When to use `concept_comparison` - You need to map combinations of sub_concepts to outcomes - Complex conditional logic benefits from compile-time validation - Behavior trees need type-safe decision points --- ## Examples ### Simple Enumeration ```storybook concept Season; sub_concept Season.Name { Spring, Summer, Fall, Winter } ``` ### Multi-faceted Type ```storybook concept Food; sub_concept Food.Type { Bread, Pastry, Cake } sub_concept Food.Freshness { Fresh, Stale, Spoiled } concept_comparison FoodQuality { Excellent: { Food.Type: any, Food.Freshness: Food.Freshness is Fresh }, Poor: { Food.Type: any, Food.Freshness: Food.Freshness is Stale or Food.Freshness is Spoiled } } ``` ### Record Type ```storybook concept Inventory; sub_concept Inventory.Record { item_type: Product, quantity: any, location: StorageArea } ``` --- ## Implementation Notes ### Parser - Sub_concept parent inference uses prefix matching on the concept name - Enumerated vs typed sub_concepts are disambiguated by presence of `:` after first identifier - `any` keyword is parsed as `Value::Any` in the AST ### AST Representation ```rust pub struct ConceptDecl { pub name: Text, pub span: Span, } pub struct SubConceptDecl { pub name: Text, pub parent_concept: Text, // Inferred from naming convention pub kind: SubConceptKind, pub span: Span, } pub enum SubConceptKind { Enum { variants: Vec }, Record { fields: Vec }, } pub struct ConceptComparisonDecl { pub name: Text, pub variants: Vec, pub span: Span, } pub struct VariantPattern { pub name: Text, pub conditions: Vec, pub span: Span, } pub enum Condition { Any, Is(Vec), // "is Value1 or Value2" } ``` --- --- ## Species and Template Inheritance ### Species as Base Types with Defaults Species define base record types with default values that templates can extend and characters can override. **Syntax:** ```storybook species SpeciesName { field: Type = default_value, ... } ``` **Example:** ```storybook species Human { bipedal: Boolean = true, has_hair: Boolean = true, base_lifespan: Number = 80, can_use_magic: Boolean = false } species Elf { bipedal: Boolean = true, pointed_ears: Boolean = true, base_lifespan: Number = 1000, can_use_magic: Boolean = true } ``` ### Template Extension Templates extend species using `:` syntax and can override species defaults or add new fields. **Syntax:** ```storybook template TemplateName: SpeciesName { field: Type = default, // Override species field new_field: Type, // Add new required field new_field2: Type = default // Add new field with default } ``` **Example:** ```storybook template Person: Human { // Inherits: bipedal, has_hair, base_lifespan, can_use_magic (with defaults) age: Number, // New required field name: Text // New required field } template Cyborg: Human { // Override species defaults base_lifespan: Number = 200, // Cyborgs live longer organic: Boolean = false, // New field // Add new fields model: Text, // Required battery_level: Number = 100 // With default } ``` ### Override Priority Chain **Character > Template > Species** When a field is defined at multiple levels, the most specific definition wins: ```storybook species Human { strength: Number = 10, intelligence: Number = 10, speed: Number = 10 } template Warrior: Human { strength: Number = 15, // Override species default weapon: Text // New required field } character Conan: Warrior { strength: 20, // Override template (which overrode species) weapon: "Greatsword", // Required by template intelligence: 5 // Override species default // speed: 10 (from species, no overrides) } ``` **Resolution order:** 1. Check character fields - use if present 2. Check template fields - use if present and not in character 3. Check species fields - use if not overridden 4. Error if no value found and field is required ### Character Instantiation with Inheritance ```storybook character Martha: Person { // Required template fields (no defaults) age: 34, name: "Martha", // Optional overrides of species defaults bipedal: false, // She has one leg can_use_magic: true, // Exceptional human // Inherited from species (use defaults) // has_hair: true (from Human) // base_lifespan: 80 (from Human) } ``` ### Benefits ✅ **Layered defaults** - species → template → character ✅ **No redundancy** - don't repeat common defaults ✅ **Composable** - templates customize species for specific roles ✅ **Exceptional cases** - characters override when needed ✅ **Clear precedence** - explicit override chain --- ## Life Arcs as Entity State ### Core Concept **Life arcs represent the current state of an entity in various state machines.** An entity (character/location/institution) is not just data - it has **state** that changes over time. Life arcs define: - What states exist (Baby, Child, Adult, Elder) - How to transition between states (when age >= 18) - What behaviors are available in each state **The entity's life arc assignments ARE its current state in those state machines.** ### Entity State Model ```storybook character Martha: Baker { // Data fields age: 34, skill_level: 8, reputation: 85, // STATE - current position in life arc state machines life_arcs: { Human: Adult, // In "Adult" state of Human state machine Baker: Master, // In "Master" state of Baker state machine Reputation: Famous // In "Famous" state of Reputation state machine } } ``` **Martha's state is:** - Age progression: Adult (can Work, Train, Manage) - Career progression: Master (can BakingWork, TrainApprentice, InnovateTechniques) - Social progression: Famous (can Influence, NetworkGlobally) ### Life Arcs Define State Machines ```storybook life_arc Human requires { age: Number } { Baby { when age < 2 can use behaviors: [Cry, Sleep, Eat] -> Child when age >= 2 } Child { when age >= 2 and age < 12 can use behaviors: [Cry, Play, Learn, Eat, Sleep] -> Adolescent when age >= 12 } Adolescent { when age >= 12 and age < 18 can use behaviors: [Cry, Sleep, Play, Learn, Socialize, Work] -> Adult when age >= 18 } Adult { when age >= 18 and age < 65 can use behaviors: [Cry, Sleep, Work, Socialize, Train, Manage] -> Elder when age >= 65 } Elder { when age >= 65 can use behaviors: [Cry, Sleep, Rest, Socialize, Mentor] } } ``` **This defines:** - **States**: Baby, Child, Adolescent, Adult, Elder - **Transitions**: age thresholds trigger state changes - **Capabilities**: each state grants different behaviors ### State Determines Capabilities **Behavior availability = Life Arc States + Location + Template** ```storybook Can Martha use TrainApprentice at Bakery? Check life arc states: ✓ Human.Adult grants ability to Train ✓ Baker.Master grants TrainApprentice Check location: ✓ Bakery enables TrainApprentice → YES - all conditions met ``` **If Martha were Baker.Journeyman instead:** ```storybook character Martha: Baker { skill_level: 5, life_arcs: { Human: Adult, Baker: Journeyman // Different state! } } Can Martha use TrainApprentice at Bakery? Check life arc states: ✓ Human.Adult grants ability to Train ✗ Baker.Journeyman does NOT grant TrainApprentice → NO - lacks capability from life arc state ``` ### Multiple Concurrent State Machines Entities can be in multiple state machines simultaneously: ```storybook life_arc Human requires { age: Number } { // Age-based progression } life_arc Baker requires { skill_level: Number } { // Skill-based progression } life_arc Reputation requires { fame: Number } { // Fame-based progression } character Martha: Baker { age: 34, skill_level: 8, fame: 85, life_arcs: { Human: Adult, // State in age dimension Baker: Master, // State in career dimension Reputation: Famous // State in social dimension } } ``` **Each life arc is an independent state machine:** - Human state machine: transitions based on age - Baker state machine: transitions based on skill_level - Reputation state machine: transitions based on fame **Available behaviors = union of all state capabilities:** - From Human.Adult: Work, Train, Manage - From Baker.Master: BakingWork, TrainApprentice - From Reputation.Famous: Influence, NetworkGlobally ### Locations Have State Too ```storybook life_arc Settlement requires { population: Number, development: Number } { Village { when population < 500 enables behaviors: [Farming, Fishing] -> Town when population >= 500 and development >= 3 } Town { when population >= 500 and population < 5000 enables behaviors: [Trading, Crafting, Farming] -> City when population >= 5000 and development >= 7 } City { when population >= 5000 enables behaviors: [Trading, Manufacturing, Politics, Education] } } location TownSquare: SettlementTemplate { population: 500, development: 4, life_arcs: { Settlement: Town // Current state } } ``` **TownSquare's state:** - It's in the "Town" state (not Village, not City) - It enables behaviors: Trading, Crafting, Farming - When population reaches 5000 AND development reaches 7, it transitions to City state ### Institutions Have State Too ```storybook life_arc Business requires { revenue: Number, reputation: Number } { Startup { when revenue < 10000 enables behaviors: [Hustle, Experiment] -> Established when revenue >= 10000 } Established { when revenue >= 10000 and revenue < 100000 enables behaviors: [Operate, Expand] -> Corporate when revenue >= 100000 } Corporate { when revenue >= 100000 enables behaviors: [Operate, Expand, Franchise, Lobby] } } institution Bakery: Building { revenue: 15000, reputation: 75, life_arcs: { Business: Established // Current state } } ``` ### Bidirectional State Transitions **State machines are NOT necessarily forward-only.** Characters can progress forward, regress backward, or transition laterally based on field changes: ```storybook life_arc Baker requires { skill_level: Number, experience: Number } { Apprentice { when skill_level < 3 can use behaviors: [Learn, AssistBaking] -> Journeyman when skill_level >= 3 } Journeyman { when skill_level >= 3 and skill_level < 7 can use behaviors: [BakingWork, ManageInventory] -> Master when skill_level >= 7 // Progress forward -> Apprentice when skill_level < 3 // Regress backward! } Master { when skill_level >= 7 can use behaviors: [BakingWork, ManageInventory, TrainApprentice, InnovateTechniques] -> Journeyman when skill_level < 7 // Can lose mastery! } } ``` **The `when` clause defines:** - **State validity condition**: What must be true to be in this state - **NOT a one-time check**: Re-evaluated whenever fields change **Examples of regression:** ```storybook character Martha: Baker { skill_level: 8, life_arcs: { Baker: Master } } // Martha is Master Baker, then loses practice action lose_practice(baker: Person) { baker.skill_level -= 5 // Now skill_level = 3 } lose_practice(Martha) // Runtime re-evaluates: // 1. Is Martha still valid for Master? (requires skill_level >= 7) // 3 >= 7? NO - state is invalid! // 2. Check Master's transitions: // -> Journeyman when skill_level < 7 // 3 < 7? YES - transition! // 3. Martha is now Journeyman // Martha's available behaviors change: // Lost: TrainApprentice, InnovateTechniques // Kept: BakingWork, ManageInventory ``` **Examples of recovery:** ```storybook // Later, Martha practices and improves action practice_baking(baker: Person) { baker.skill_level += 1 } // Practice multiple times practice_baking(Martha) // skill_level = 4 practice_baking(Martha) // skill_level = 5 practice_baking(Martha) // skill_level = 6 practice_baking(Martha) // skill_level = 7 // After skill_level reaches 7: // Runtime re-evaluates: // 1. Check Journeyman's transitions: // -> Master when skill_level >= 7 // 7 >= 7? YES - transition! // 2. Martha is Master again! ``` ### State Changes = Mutations **Life arcs ARE the mutability mechanism:** 1. **Actions modify field values:** ```storybook action practice_baking(baker: Person) { // Runtime implementation baker.skill_level += 1 } action lose_practice(baker: Person) { // Runtime implementation baker.skill_level -= 1 } ``` 2. **Modified values trigger state transitions (in ANY direction):** ```storybook // Progression // Before: Martha.skill_level = 6, Baker state = Journeyman practice_baking(Martha) // After: Martha.skill_level = 7 // Transition: Journeyman -> Master (forward) // Regression // Before: Martha.skill_level = 7, Baker state = Master lose_practice(Martha) lose_practice(Martha) lose_practice(Martha) // After: Martha.skill_level = 4 // Transition: Master -> Journeyman (backward) ``` 3. **New state grants/removes capabilities:** ```storybook // After progression to Master: // Gained: TrainApprentice, InnovateTechniques // After regression to Journeyman: // Lost: TrainApprentice, InnovateTechniques // Kept: BakingWork, ManageInventory ``` ### Runtime State Re-evaluation **After ANY action that modifies fields:** ```rust impl Runtime { fn execute_action(&mut self, action: Action) { // 1. Execute action (mutates entity fields) let entity_id = match &action { Action::PracticeBaking { baker } => { let character = self.get_character_mut(*baker); character.skill_level += 1; *baker } Action::LosePractice { baker } => { let character = self.get_character_mut(*baker); character.skill_level -= 1; *baker } _ => { // Handle other actions return; } }; // 2. Re-evaluate ALL life arc states for this entity self.update_life_arc_states(entity_id); } fn update_life_arc_states(&mut self, entity_id: EntityId) { let entity = self.get_character(entity_id); let life_arcs = entity.life_arcs.clone(); for (arc_name, current_state) in &life_arcs { let arc = self.get_life_arc(arc_name); let state_def = arc.get_state(current_state); // Check if current state condition is still valid if !self.evaluate_condition(entity_id, &state_def.condition) { // State is invalid - find valid transition for transition in &state_def.transitions { if self.evaluate_condition(entity_id, &transition.condition) { // Transition to new state let entity = self.get_character_mut(entity_id); entity.life_arcs.insert(arc_name.clone(), transition.to.clone()); // Log transition println!( "Entity {} transitioned: {}.{} -> {}.{}", entity.name, arc_name, current_state, arc_name, transition.to ); break; } } } } } } ``` **Re-evaluation algorithm:** 1. After action execution, check ALL life arcs for the affected entity 2. For each life arc, evaluate current state's `when` condition 3. If condition is FALSE, current state is invalid 4. Check state's transitions in order 5. Take first transition whose condition is TRUE 6. Update entity's life_arcs to new state 7. New state's capabilities are now available **Design principle:** State follows data. When field values change, states automatically transition to match the new reality. ### Mental Model **Think of entities as:** - **Data**: Fields with values (age, skill_level, reputation) - **State**: Current position in life arc state machines - **Capability**: Behaviors available based on state + location + template **Life arcs are NOT metadata - they ARE the state.** When you ask "What can Martha do?", you're asking: 1. What state is she in? (Human.Adult, Baker.Master) 2. Where is she? (Bakery) 3. What does her template grant? (Baker template) The answer is the intersection of capabilities from all three layers. --- --- ## Type System Soundness ### Type Theory Foundations Storybook uses a **hybrid type system** combining: - **Nominal typing**: Templates and entities have explicit names and identity - **Structural typing**: Life arc requirements use duck typing (any template with required fields) - **Subtype polymorphism**: Template extension creates subtype relationships - **Algebraic types**: Concepts and sub_concepts form sum types with pattern matching ### Subtyping Relationship **Template extension creates subtyping:** ```storybook species Human { strength: Number = 10 } template Person: Human { age: Number } template Baker: Person { skill_level: Number } Type hierarchy: Baker <: Person <: Human ``` **Liskov Substitution Principle:** - Baker can be used wherever Person is expected (has all Person fields) - Person can be used wherever Human is expected (has all Human fields) - Subtyping flows from child to parent (upcast is implicit and safe) **Action parameter compatibility:** ```storybook action greet(person: Person) character Martha: Baker greet(Martha) // ✓ Valid - Baker <: Person (implicit upcast) action train_baker(baker: Baker) character Bob: Person train_baker(Bob) // ✗ ERROR - Person Template > Species** ```storybook species Human { strength: Number = 10, speed: Number = 10 } template Warrior: Human { strength: Number = 15 } character Conan: Warrior { strength: 20 } Field lookup algorithm: Conan.strength: 1. Check character instance → 20 ✓ FOUND 2. (not reached) Conan.speed: 1. Check character instance → not found 2. Check Warrior template → not found 3. Check Human species → 10 ✓ FOUND ``` **Type invariance through chain:** - Field types cannot change through inheritance - If species defines `strength: Number`, template cannot override to `strength: Text` - Only values/defaults can be overridden, not types ### Life Arc Type Constraints **Life arcs use structural requirements (duck typing):** ```storybook life_arc Aging requires { age: Number, species: Species } // Works with ANY template that has these fields ``` **Compile-time validation:** ```storybook template Person: Human { age: Number, // ✓ has required field name: Text, // species inherited from Human ✓ has required field } character Martha: Person { life_arcs: { Aging: Adult // ✓ Person has all required fields } } // Compiler checks: // 1. Does Person have field 'age' of type Number? YES // 2. Does Person have field 'species' of type Species? YES (inherited) // 3. Are types exact match (not subtypes)? YES // → VALID ``` **Unsoundness prevention:** ```storybook template Robot { age?: Number // Optional field } life_arc Aging requires { age: Number } { // Requires NON-optional age } character R2D2: Robot { life_arcs: { Aging: Adult } } // ✗ TYPE ERROR: Aging requires age: Number (required) // but Robot has age?: Number (optional) // Required field cannot be optional in template ``` **Rule:** Life arc required fields MUST be non-optional in template. ### Behavior Type Checking **Implicit self with template typing:** ```storybook behavior BakingWork { then { check_orders // Desugars to: check_orders(self) bake(Bread) // Desugars to: bake(self, Bread) } } action check_orders(baker: Baker) action bake(baker: Baker, item: Item) ``` **Type of implicit self:** - `self` has the type of the entity's template - Known at compile-time **Compatibility checking:** ```storybook character Martha: Baker { uses behaviors: [BakingWork] } // Compiler validates: // When Martha runs BakingWork: // self: Baker (Martha's template) // check_orders(self) → check_orders(Baker) ✓ // bake(self, Bread) → bake(Baker, Item) ✓ // → VALID character Bob: Person { uses behaviors: [BakingWork] } // Compiler validates: // When Bob tries to run BakingWork: // self: Person (Bob's template) // check_orders(self) → check_orders(Person) // But check_orders expects Baker // Person = 7 // Entry condition + runtime assertion can use behaviors: [InnovateTechniques] } } ``` **The `when` clause serves two purposes:** 1. **Entry condition**: Must be true to transition INTO this state 2. **Runtime assertion**: Should be true while IN this state **Potential inconsistency:** ```storybook character Martha: Baker { skill_level: 8, life_arcs: { Baker: Master } // State says Master } action lower_skill(baker: Baker) { baker.skill_level -= 5 // Now skill_level = 3 } // After action: Martha is in Master state, but skill_level < 7 // State is inconsistent with data! ``` **Resolution - Hybrid approach:** **Compile-time (Storybook):** Emit warnings when actions could invalidate state conditions: ``` Warning: action 'lower_skill' modifies 'skill_level' which is used in state condition for Baker.Master (requires skill_level >= 7). Runtime should validate state before executing state-specific behaviors. ``` **Runtime (Implementation):** Before executing behaviors that depend on state: ```rust impl Runtime { fn execute_behavior(&mut self, entity_id: EntityId, behavior: &Behavior) { let character = self.get_character(entity_id); // Validate current state conditions for (arc_name, current_state) in &character.life_arcs { let arc = self.get_life_arc(arc_name); let state_def = arc.get_state(current_state); if !self.evaluate_condition(entity_id, &state_def.condition) { // State is inconsistent! // Option A: Re-evaluate and transition to correct state // Option B: Log warning and continue // Option C: Throw error and reject behavior // Runtime decides policy } } // Execute behavior actions... } } ``` **Rule:** State conditions are validated at entry (transition time) and SHOULD be validated at execution time (runtime's responsibility). ### Type Checking Guarantees **Compile-time guarantees:** ✅ Template subtyping is safe (Baker <: Person) ✅ Field access through inheritance is valid ✅ Life arc structural requirements are satisfied ✅ Action parameters match expected types ✅ Behaviors are compatible with entity templates ✅ Initial life arc states satisfy entry conditions **Runtime responsibilities:** ⚠️ Validate state conditions before executing state-specific behaviors ⚠️ Handle state transitions when field values change ⚠️ Manage entity state consistency **The language guarantees type safety at compile-time. The runtime guarantees state consistency at execution-time.** ### Formal Type Rules **Subtyping (transitive):** ``` T modifies S ─────────────── (SUB-EXTEND) T <: S T <: S S <: R ─────────────── (SUB-TRANS) T <: R ``` **Field access:** ``` E : T T has field f : τ (through inheritance chain) ──────────────────────────────────────────────────── (FIELD-ACCESS) E.f : τ ``` **Life arc compatibility:** ``` T has fields {f1: τ1, ..., fn: τn} (required, non-optional) L requires {f1: τ1, ..., fn: τn} ──────────────────────────────────────────────────── (LIFEARC-COMPAT) T compatible with L ``` **Behavior validity:** ``` B calls actions {a1(Self, ...), ..., an(Self, ...)} E : T ∀i. T <: ParamType(ai, 0) (template is subtype of first param) ──────────────────────────────────────────────────── (BEHAVIOR-VALID) E can use behavior B ``` **Action call:** ``` a : (T1, T2, ..., Tn) → Unit E : T T <: T1 args : (T2, ..., Tn) ──────────────────────────────────────────────────── (ACTION-CALL) a(E, args) : Unit ``` ### Summary The type system is **sound** with these properties: - **Progress**: Well-typed programs don't get stuck at compile-time - **Type preservation**: Types are maintained through inheritance and execution - **Memory safety**: No invalid field access or type confusion - **Subtype safety**: Upcasting (child → parent) is safe and implicit The runtime must maintain **state consistency** by validating life arc conditions at execution time. --- ## Future Considerations - **Exhaustiveness checking**: Ensure all variant combinations are covered in concept_comparisons - **Default variants**: Support for catch-all patterns in comparisons - **Type inference**: Automatic parent concept detection beyond prefix matching - **Generic concepts**: Parameterized types for reusable patterns - **Constraint validation**: Runtime validation of type constraints