# Modules Modules and paths are fundamental concepts for code organization, namespace management, and the ability to share and reuse code across projects. Think of modules as containers that group related functionality together, similar to packages in other programming languages, but with stronger guarantees about versioning and compatibility. In the context of game development, modules allow you to separate different aspects of your game logic into manageable, reusable pieces. For example, you might have one module for player inventory management, another for combat mechanics, and yet another for UI interactions. Each module encapsulates its own functionality while exposing only the necessary interfaces to other parts of your code. The module system is designed to support the vision of a persistent, shared Metaverse where code can be published once and used by anyone, anywhere, with confidence that it will continue to work even as the original author updates and improves it. This is achieved through strict backward compatibility rules and a global namespace system that ensures every piece of published code has a unique, permanent address. Each module is intrinsically linked to the file system structure of your project. When you create a folder in your Verse project, that folder automatically becomes a module. The module's name is simply the folder's name, making the relationship between your file organization and your code organization completely transparent. All `.verse` files within the same folder are considered part of that module and share the same namespace. This means that if you have three files - `player.verse`, `inventory.verse`, and `equipment.verse` - all in a folder called `PlayerSystems`, they all contribute to the `PlayerSystems` module and can reference each other's definitions without any import statements. This automatic grouping makes it easy to split large modules across multiple files for better organization while maintaining the logical unity of the module. ## Paths Paths are the addressing system that makes Verse's vision of a shared, persistent Metaverse possible. Just as every website on the internet has a unique URL, every module has a unique path that identifies it globally. This path system is more than just a naming convention - it's a fundamental part of how Verse manages code distribution, versioning, and dependencies. Paths borrow conceptually from web domains with adaptations for the needs of a programming language. A path starts with a forward slash `/` and typically includes a domain-like identifier followed by one or more path segments. This creates a hierarchical namespace that is both human-readable and globally unique. The format `/domain/path/to/module` serves several important purposes: - **Persistent and unique identification**: Once a module is published at a particular path, that path belongs to it forever. No other module can ever claim the same path, ensuring that dependencies always resolve to the correct code. - **Ownership and authority**: The domain portion of the path (like `Fortnite.com` or `Verse.org`) indicates who owns and maintains the module. This helps developers understand the source and trustworthiness of the code they're using. - **Discoverability**: Because paths follow a predictable pattern, developers can often guess or easily find the modules they need. Documentation and tooling can also leverage this structure to provide better discovery experiences. - **Hierarchical organization**: The path structure naturally supports organizing related modules together. For example, all UI-related modules might live under `/YourGame.com/UI/`, making them easy to find and understand as a group. Epic Games provides several standard modules that are commonly used: - `/Verse.org/Verse` - Core language features and standard library functions - `/Verse.org/Random` - Random number generation utilities - `/Verse.org/Simulation` - Simulation and timing utilities - `/Fortnite.com/Devices` - Integration with Fortnite Creative devices - `/UnrealEngine.com/Temporary/Diagnostics` - Debugging and diagnostic tools - `/UnrealEngine.com/Temporary/SpatialMath` - 3D math and spatial operations The use of "Temporary" in some paths indicates that these modules are provisional and may be reorganized in future versions of Verse. This naming convention helps set expectations about the stability of the API. When you create your own modules, they can exist at various levels of the path hierarchy: - `/YourGame/` - Top-level module for your game - `/YourGame/Player/` - Player-related functionality - `/YourGame/Player/Inventory/` - Specific inventory management - `/pizlonator@fn.com/NightDeath/` - Personal or experimental modules The ability to include email-like identifiers (such as `pizlonator@fn.com`) allows individual developers to claim their own namespace without needing to own a domain. This democratizes the module system while still maintaining uniqueness guarantees. ## Creating Modules A module can contain: - Constants and variables - Functions - Classes, interfaces, and structs - Enums - Other module definitions - Type definitions When you create a subfolder in a Verse project, a module is automatically created for that folder. The file structure directly maps to the module hierarchy. You can create modules within a `.verse` file using the following syntax: ```verse # Colon syntax module1 := module: # Module contents here MyConstant:int = 42 MyClass := class: Value:int = 0 # Bracket syntax (also supported) module2 := module { # Module contents here AnotherConstant:string = "Hello" } ``` Modules can contain other modules, creating a hierarchy: ```verse BaseModule := module: submodule := module: submodule_class := class: Value:int = 100 module_class := class: Name:string = "" ``` The file structure `ModuleFolder/BaseModule` is equivalent to: ```verse ModuleFolder := module: BaseModule := module: Submodule := module: submodule_class := class: # Class definition ``` ### Restrictions Module bodies have strict requirements about what they can contain. Understanding these restrictions helps avoid common errors when defining modules. **Modules Can Only Contain Definitions:** A module body can only contain definition statements—declarations that bind names to values. You cannot include arbitrary expressions or executable statements: ```verse # Valid: All definitions Config := module: MaxValue:int = 100 DefaultName:string = "Player" CalculateScore(Base:int):int = Base * 10 player_class := class: Name:string # Invalid: Contains non-definition expressions BadModule := module: MaxValue:int = 100 1 + 2 # ERROR 3560: Not a definition # Invalid: Contains function call BadModule2 := module: InitFunction():void = {} InitFunction() # ERROR 3585: Cannot call function in module body ``` The restriction ensures that module initialization is deterministic and doesn't execute arbitrary code when the module is loaded. **Type Annotations Required:** All data definitions at module scope must explicitly specify their type. Type inference with `:=` alone is not allowed: ```verse # Invalid: Missing type annotation BadModule := module: Value := 42 # ERROR 3547: Must specify type domain # Valid: Explicit type annotation GoodModule := module: Value:int = 42 # OK: Type explicitly specified ``` This requirement makes module interfaces explicit and helps with separate compilation and module evolution. **Valid Module Contents:** Modules can contain these categories of definitions: ```verse Utilities := module: # Constants with explicit types Version:int = 1 AppName:string = "MyApp" # Functions Calculate(X:int):int = X * 2 # Classes, interfaces, structs data_class := class: Value:int data_interface := interface: GetValue():int data_struct := struct: X:float Y:float # Enums status := enum: Active Inactive # Nested modules Nested := module: NestedFunction():void = {} # Type aliases coordinate := tuple(float, float) # Refinement types positive_int := type{X:int where X > 0} ``` Unlike functions, classes, or data values, modules are not first-class citizens in Verse. You cannot treat modules as values that can be stored, passed, or manipulated at runtime. **Cannot Assign Modules to Variables:** ```verse MyModule := module: Value:int = 42 # Invalid: Cannot treat module as value M:MyModule = MyModule # ERROR ``` Modules exist purely as namespaces and organizational constructs at compile time. The module identifier `MyModule` can only be used in specific contexts. **Cannot Pass Modules as Arguments:** ```verse MyModule := module: X:int = 1 # Invalid: Cannot pass module as parameter ProcessModule(M:module):void = {} # ERROR ProcessModule(MyModule) # ERROR ``` There is no `module` type that can be used in function signatures. **Cannot Create Collections of Modules:** ```verse ModuleA := module: Value:int = 1 ModuleB := module: Value:int = 2 # Invalid: Cannot create tuple or array of modules Modules := (ModuleA, ModuleB) # ERROR ``` ## Importing Modules The import system is designed to be explicit and predictable. Unlike some languages that automatically import commonly used modules or search multiple locations for dependencies, Verse requires you to explicitly declare every external module you want to use. This explicitness helps prevent naming conflicts and makes dependencies clear. The `using` statement is the primary mechanism for importing modules into your Verse code. It usually is placed at the top of your file, before any other code definitions, and makes the contents of the specified module available in your current scope. The basic syntax is straightforward - the keyword `using` followed by the module path in curly braces: ```verse using { /Verse.org/Random } using { /Fortnite.com/Devices } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } ``` When you import a module, all its public members become available in your code. However, you still need to qualify them with the module name unless the names are unambiguous. This qualification requirement helps maintain code clarity and prevents accidental use of the wrong definition when multiple modules define similar names. **Using is a Statement, Not an Expression:** The `using` directive is a statement-level declaration that must appear at the top level of your code. You cannot use it as an expression or embed it in other expressions: ```verse # Invalid: using in expression context # f():void = using{MyModule} # ERROR 3669 # Invalid: using in conditional # if (using{MyModule}, Condition?): # DoSomething() # ERROR 3669 # Invalid: using in class/struct/interface body # my_class := class: # using{MyModule} # ERROR 3537 # Field:int # Invalid: using module path in function body # ProcessData():void = # using{/MyProject/UtilityModule} # ERROR 3669 # Calculate() ``` Module `using` statements must appear at the file or module level, not nested within other constructs. This ensures that imports are visible and consistent throughout the scope where they're declared. While module imports with paths are not allowed in function bodies, Verse does support **local scope `using`** with local variables and parameters. See [Local Scope Using](#local-scope-using) below for details. **Valid using placement:** ```verse # At file level (most common) using { /Verse.org/Random } using { /Verse.org/Simulation } ProcessData():void = # Use imported functions Value := GetRandomFloat(0.0, 1.0) # Within module definition Utilities := module: using { /Verse.org/Random } GenerateId():int = GetRandomInt(1, 1000000) ``` ### Import Resolution When Verse encounters a `using` statement, it follows a specific resolution process: 1. **Absolute paths** (starting with `/`) are resolved from the global module registry 2. **Relative paths** (without leading `/`) are resolved relative to the current module's location 3. **Nested modules** can be accessed through their parent modules This resolution process happens at compile time, meaning that all imports must be resolvable when your code is compiled. There's no runtime module loading or dynamic imports in Verse. ### Local and Relative Imports For modules within your own project, you have flexibility in how you reference them: ```verse # Absolute import from your project root using { /MyGameProject/Systems/Combat } # Import from a sibling folder using { ../UI/MainMenu } # Import from the same directory using { PlayerController } # Import from a subdirectory using { Subsystems/WeaponSystem } ``` The choice between absolute and relative imports often depends on your project structure and whether you plan to reorganize your modules. Absolute imports are more stable when refactoring, while relative imports can make module groups more portable. ### Nested Imports Nested modules present special considerations for importing. The order in which you import modules matters, and there are multiple valid approaches: ```verse # Method 1: Import parent first, then child using { GameSystems } using { Inventory } # Assumes Inventory is nested in GameSystems # Method 2: Direct path to nested module using { GameSystems.Inventory } # Method 3: Import parent and access child through qualification using { GameSystems } # Later in code: GameSystems.Inventory.Item # IMPORTANT: This order causes an error # using { Inventory } # Error: Inventory not found # using { GameSystems } # Too late, Inventory import already failed ``` The restriction on import order exists because Verse resolves imports sequentially. When you import a nested module directly, Verse needs to know about its parent module first. This is why importing the parent before the child always works, while the reverse order fails. ### Module Aliases with import The `import` expression creates a local alias for a module, binding its path to a name. Unlike `using`, which brings a module's public members directly into scope, `import` lets you access them through the alias with dot notation: ```verse # using: members available directly using { /MyProject/Utilities } Result := HelperFunction() # HelperFunction is in scope # import: members accessed through alias Utils := import(/MyProject/Utilities) Result := Utils.HelperFunction() # accessed via alias ``` This is useful when you want to avoid name collisions, or when you need to make the origin of a definition explicit in your code: ```verse Physics := import(/MyProject/Systems/Physics) Graphics := import(/MyProject/Systems/Graphics) # Clear which Transform is being used PhysicsTransform := Physics.Transform{} GraphicsTransform := Graphics.Transform{} ``` Module aliases created with `import` are visible across all snippets within the same module. An `import` can also be combined with `using` to both alias a module and bring its members into scope: ```verse Graphics := import(/MyProject/Systems/Graphics) using { Graphics } # now Graphics members are also directly available ``` Note that `import` only works with module paths. Attempting to import a path that resolves to a class or other non-module definition is an error. ### Scope and Visibility Imports have file scope - they only affect the file in which they appear. If you have multiple `.verse` files in the same module, each file needs its own import statements for external modules. However, files within the same module can see each other's definitions without imports: ```verse # File: player_module/health.verse health_component := class: CurrentHealth:float = 100.0 # File: player_module/armor.verse # No import needed for health_component since it's in the same module armor_component := class: HealthComp:health_component = health_component{} ``` ### Import Conflicts When two imported modules define members with the same name, you need to disambiguate: ```verse using { /GameA/Combat } using { /GameB/Combat } # Both modules might define CalculateDamage # You must use qualified names: DamageA := Combat.CalculateDamage(10.0) # Error: ambiguous DamageA := (/GameA/Combat:)CalculateDamage(10.0) # OK: fully qualified DamageB := (/GameB/Combat:)CalculateDamage(10.0) # OK: fully qualified ``` ### Qualified Names After importing, you can refer to module contents using qualified names. Verse provides two forms of qualification: standard dot notation for most cases, and special qualified access syntax for disambiguation. When you need to disambiguate between identifiers with the same name from different modules, or when you want to explicitly specify the scope of an identifier, use a qualified access expression using parentheses and a colon: ```verse # Qualified access syntax: (qualifier:)identifier using { CombatModule } using { MagicModule } ProcessDamage():void = # Both modules define CalculateDamage PhysicalDamage := (CombatModule:)CalculateDamage(100.0) MagicalDamage := (MagicModule:)CalculateDamage(100.0) # Explicitly qualify local vs module identifiers LocalItem := item{Name := "Sword"} # Local definition ModuleItem := (InventoryModule:)item{Name := "Shield"} # From module ``` The qualified access expression `(module:)identifier` is particularly useful in several scenarios: 1. **Resolving naming conflicts**: When multiple imported modules export the same identifier 2. **Explicit scoping**: When you want to make it clear which module an identifier comes from for readability 3. **Accessing shadowed names**: When a local definition shadows a module member 4. **Generic programming**: When working with parametric types where the qualifier might be computed ## Module-Scoped Variables Variables defined at module scope are global to any game instance where the variable is in scope. Restrictions on module-scoped definitions: - Direct `var` declarations of simple types (like `var X:int = 0`) are not allowed at module scope - Instances of `` classes with `` can be created at module scope, as long as their construction doesn't actually allocate mutable memory - For persistent mutable state, use `weak_map` with appropriate key types (see below) Use `weak_map(session, t)` for variables that persist for the duration of a game session: ```verse var GlobalCounter:weak_map(session, int) = map{} IncrementCounter():void = CurrentValue := if (Value := GlobalCounter[GetSession()]) then Value + 1 else 0 if (set GlobalCounter[GetSession()] = CurrentValue) {} ``` Use `weak_map(player, t)` for data that persists across game sessions: ```verse var PlayerSaveData:weak_map(player, player_data) = map{} player_data := class: Level:int = 1 Experience:int = 0 UnlockedItems:[]string = array{} SavePlayerProgress(Player:player, NewData:player_data):void = set PlayerSaveData[Player] = NewData ``` ## Metaverse and Publishing When you publish a module to the Metaverse, the module path becomes globally accessible, its public members become part of the module's API, and from that point the module must maintain backward compatibility. The following example of shows how evolution works: ```verse # Initial publication Thing:rational = 1/3 # Valid updates: # - Change the value (not the type) Thing:rational = 10/3 # - Make the type more specific (subtype) Thing:int = 20 # nat is a subtype of int # Invalid updates (would be rejected): # - Remove the member # - Change to incompatible type # Thing:string = "hello" # Would fail ``` ## Local Qualifiers The `(local:)` qualifier can disambiguate identifiers within functions. This is critical for evolution compatibility—when external modules add new public definitions after your code is published, `(local:)` ensures your local definitions take precedence. ```verse # External module adds ShadowX after your code published ExternalModule := module: ShadowX:int = 10 # Added later! MyModule := module: using{ExternalModule} # Without (local:) - shadowing conflict # Foo():float = # ShadowX:float = 0.0 # Error: conflicts with ExternalModule.ShadowX # ShadowX # With (local:) - clear disambiguation Foo():float = (local:)ShadowX:float = 0.0 # Local variable (local:)ShadowX # Returns 0.0, not 10 ``` The `(local:)` qualifier can be used in these contexts: **Function parameters:** ```verse ProcessValue((local:)Value:int):int = (local:)Value + 1 ``` **Function body data definitions:** ```verse Compute():int = (local:)Result:int = 42 (local:)Result ``` **For loop variables:** ```verse SumValues():int = var Total:int = 0 for ((local:)I := 0..10): set Total += (local:)I Total ``` **If conditions:** ```verse CheckValue():float = if (X := GetValue[], (local:)X > 5.0): (local:)X else: 0.0 ``` **Block scopes:** ```verse ComputeInBlock():int = block: (local:)Temp:int = 10 (local:)Temp * 2 ``` **Class blocks:** ```verse my_class := class: var Value:int = 0 block: (local:)Value:int = 42 set (my_class:)Value = (local:)Value ``` The `(local:)` qualifier **cannot** be used in these contexts: **Nested Scope Limitation:** Currently, you **cannot** redefine a `(local:)` qualified identifier in nested blocks: ```verse # Error: cannot redefine local identifier F((local:)X:int):int = block: (local:)X:float = 5.5 # Error: X already defined in function (local:)X ``` This limitation may be lifted in future versions to support more complex scoping patterns. ## Automatic Qualification !!! warning "Unreleased Feature" Automatic qualification has not yet been fully implemented. This section documents planned functionality that is not currently available. The behavior described here, particularly regarding how the compiler transforms identifiers in published code, should not be relied upon until officially released. When you write Verse code, you use simple, unqualified identifiers for clarity and readability. However, the Verse compiler will internally transform all identifiers into fully-qualified forms that explicitly specify their scope and origin. This process, called **automatic qualification**, will ensure that every identifier is unambiguous and can be resolved to exactly one definition. Understanding automatic qualification will help you understand how Verse will resolve names, why certain errors occur, and how the module system will maintain correctness even in complex codebases with many modules and overlapping names. The compiler will qualify several categories of identifiers: 1. **Top-level definitions** - Functions, variables, classes, modules at package scope 2. **Type references** - All types, including built-in types like `int` and `string` 3. **Function parameters** - Local parameters get the `(local:)` qualifier 4. **Class and interface members** - Methods, fields, nested within composite types 5. **Module members** - Public and internal definitions within modules 6. **Nested scopes** - References within nested modules, classes, and functions Verse uses several patterns to qualify identifiers based on their scope: **Package-level qualification**: Definitions at the root of a package are qualified with the package path: ```verse # What you write: Function(X:int):int = X # How the compiler sees it: (/YourPackage:)Function((local:)X:(/Verse.org/Verse:)int):(/Verse.org/Verse:)int = (local:)X ``` The package path `/YourPackage` becomes the qualifier for `Function`, while the parameter `X` gets the special `(local:)` qualifier, and the built-in type `int` is qualified with its standard library path `/Verse.org/Verse`. **Local scope qualification**: Function parameters and local variables are marked with `(local:)`: ```verse # What you write: ProcessValue(Input:int, Multiplier:int):int = Input * Multiplier # How the compiler sees it: (/YourPackage:)ProcessValue((local:)Input:(/Verse.org/Verse:)int, (local:)Multiplier:(/Verse.org/Verse:)int):(/Verse.org/Verse:)int = (local:)Input * (local:)Multiplier ``` **Nested scope qualification**: Members within classes, interfaces, or modules get qualified with their container's path: ```verse # What you write: player_class := class: Health:float = 100.0 TakeDamage(Amount:float):void = set Health = Health - Amount # How the compiler sees it: (/YourPackage:)player_class := class: (/YourPackage/player_class:)Health:(/Verse.org/Verse:)float = 100.0 (/YourPackage/player_class:)TakeDamage((local:)Amount:(/Verse.org/Verse:)float):(/Verse.org/Verse:)void = set (/YourPackage/player_class:)Health = (/YourPackage/player_class:)Health - (local:)Amount ``` Notice how `Health` and `TakeDamage` are qualified with `/YourPackage/player_class` to indicate they're members of the class. **Module member qualification**: Definitions within modules are qualified with the module path: ```verse # What you write: Config := module: MaxPlayers:int = 100 GetPlayerLimit():int = MaxPlayers # How the compiler sees it: (/YourPackage:)Config := module: (/YourPackage/config:)MaxPlayers:(/Verse.org/Verse:)int = 100 (/YourPackage/config:)GetPlayerLimit():(/Verse.org/Verse:)int = (/YourPackage/config:)MaxPlayers ``` All built-in types are qualified with their standard library paths. This makes it explicit where these types come from and maintains consistency with user-defined types: ```verse # Common built-in types and their full qualifications: int → (/Verse.org/Verse:)int float → (/Verse.org/Verse:)float string → (/Verse.org/Verse:)string logic → (/Verse.org/Verse:)logic message → (/Verse.org/Verse:)message ``` When you write `X:int`, the compiler expands it to `X:(/Verse.org/Verse:)int`, making the type's origin explicit. ### Example Here's a more realistic example showing how qualification would work across multiple scopes: ```verse # What you write: GameSystem := module: BaseValue:int = 42 Calculator := module: Multiplier:int = 2 Calculate(Input:int):int = Input * Multiplier + BaseValue # How the compiler will see it (when implemented): (/YourGame:)GameSystem := module: (/YourGame/GameSystem:)BaseValue:(/Verse.org/Verse:)int = 42 (/YourGame/GameSystem:)Calculator := module: (/YourGame/GameSystem/Calculator:)Multiplier:(/Verse.org/Verse:)int = 2 (/YourGame/GameSystem/Calculator:)Calculate((local:)Input:(/Verse.org/Verse:)int):(/Verse.org/Verse:)int = (local:)Input * (/YourGame/GameSystem/Calculator:)Multiplier + (/YourGame/GameSystem:)BaseValue ``` Notice how: - The parameter `Input` is `(local:)` - `Multiplier` is qualified with its containing module path - `BaseValue` is qualified with the outer module path - All type references are qualified with the Verse standard library path **Important Note on Shadowing**: Automatic qualification will only apply to published code, not your source code. Verse currently enforces strict anti-shadowing rules to prevent confusion and maintain code clarity. For example, this code does **not** compile: ```verse # This does NOT compile - shadowing is not allowed Thing := module: Thing := module: # ERROR: Cannot shadow outer Thing Potato := module{} ``` Even with automatic qualification, nested definitions cannot shadow outer definitions with the same name. If you want to intentionally shadow something, you must use explicit qualifiers to make your intent clear. This strict approach helps prevent bugs and makes code evolution safer. ### Qualification with Using When you import modules with `using`, the compiler still qualifies all identifiers, but it can resolve unqualified names to the imported modules: ```verse # What you write: using { /Verse.org/Random } GenerateRandomValue():float = GetRandomFloat(0.0, 1.0) # How the compiler sees it: using { /Verse.org/Random } (/YourGame:)GenerateRandomValue():(/Verse.org/Verse:)float = (/Verse.org/Random:)GetRandomFloat(0.0, 1.0) ``` The compiler resolves `GetRandomFloat` to `(/Verse.org/Random:)GetRandomFloat` based on the `using` statement. ### When It Matters Once implemented, you will rarely need to think about automatic or manual qualification during normal development, as the compiler will handle it transparently. However, understanding it will help in several situations: **Debugging name resolution errors**: When the compiler reports ambiguous or unresolved identifiers, understanding qualification helps you see why: ```verse using { /ModuleA } using { /ModuleB } # Both modules define Calculate Result := Calculate(10) # ERROR: Ambiguous - could be either module ``` The error occurs because the compiler cannot automatically qualify `Calculate` - it could be either `(/ModuleA:)Calculate` or `(/ModuleB:)Calculate`. **Shadowing conflicts**: When a local variable has the same name as a module member: ```verse MyModule := module: Value:int = 100 Process(Value:int):int = # Without explicit qualification, this is ambiguous Value + Value # Which Value? Module or parameter? ``` The compiler needs qualification to distinguish `(/MyModule:)Value` from `(local:)Value`. **Understanding error messages**: Compiler error messages sometimes show qualified names to precisely identify which definition is involved: ``` Error: Cannot assign (/Verse.org/Verse:)string to (/Verse.org/Verse:)int at line 42 ``` This makes it clear that the error involves the built-in `string` and `int` types, not user-defined types with the same names. **Working with generated or reflected code**: Tools that generate Verse code or analyze code structure work with the qualified form, so understanding it helps when working with such tools. ### Explicit Qualification While the compiler automatically qualifies identifiers, you can also explicitly qualify them using the qualified access syntax `(qualifier:)identifier`. This is useful when you want to override automatic resolution or make your intent explicit: ```verse GameSystem := module: Value:int = 100 # Explicitly qualify to avoid any ambiguity GetValue():int = (GameSystem:)Value # Use local qualifier for parameters SetValue((local:)Value:int):void = set (GameSystem:)Value = (local:)Value ``` Explicit qualification is particularly valuable when: - Resolving naming conflicts between imported modules - Making code more self-documenting - Overriding shadowing behavior - Working with dynamic or computed qualifiers ## Local Scope Using While module-level `using` imports modules by their paths, Verse also supports **local scope `using`** within function bodies to enable member access inference from local variables and parameters. This feature makes code cleaner when working with objects that have many member accesses. Local scope `using` takes a local variable or parameter identifier (not a module path) and makes its members accessible without explicit qualification: ```verse entity := class: Name:string = "Entity" var Health:int = 100 UpdateHealth(Amount:int):void = set Health = Health + Amount ProcessEntity(E:entity):void = # Explicit member access Print(E.Name) E.UpdateHealth(-10) Print("{E.Health}") # With local using - inferred member access using{E} Print(Name) # Inferred as: E.Name UpdateHealth(-10) # Inferred as: E.UpdateHealth(-10) Print("{Health}") # Inferred as: E.Health ``` The `using{E}` expression makes all members of `E` accessible without the `E.` prefix within the current scope. ### With Local Variables Local `using` works with variables defined in the same function: ```verse CreateAndProcess():void = Player := player{Name := "Alice", Score := 100} # Without using Print(Player.Name) set Player.Score = Player.Score + 50 # With using using{Player} Print(Name) # Inferred as: Player.Name set Score = Score + 50 # Inferred as: Player.Score ``` ### Block Scoping The `using` scope is limited to the block where it appears and any nested blocks: **Using in same block:** ```verse ProcessData():void = block: Data := data_record{} using{Data} UpdateField(Value) # Inferred as: Data.UpdateField(Data.Value) # Data members no longer accessible here ``` **Using from outer block:** ```verse ProcessData():void = Data := data_record{} block: using{Data} # Can use variable from outer scope UpdateField(Value) # Works - Data in scope ``` **Nested block inheritance:** ```verse ProcessData():void = Data := data_record{} using{Data} # Applies to this block and nested blocks block: # Inner block inherits outer using UpdateField(Value) # Still infers Data.UpdateField(Data.Value) ``` ### Order Member inference only works **after** the `using` expression is encountered: ```verse # ERROR: Cannot infer before using ProcessData(Data:data_record):void = UpdateField() # ERROR - before using using{Data} UpdateField() # OK - after using # ERROR: Using scope doesn't extend backward ProcessData(Data:data_record):void = block: using{Data} UpdateField() # OK - within using scope UpdateField() # ERROR - after using scope ended ``` The `using` statement acts as a declaration point - inference is not retroactive. ### Conflict Resolution You can have multiple `using` expressions in the same scope, but conflicting member names must be explicitly qualified: ```verse player_stats := class: Health:int = 100 Mana:int = 50 GetInfo():string = "Player" enemy_stats := class: Health:int = 80 Armor:int = 20 GetInfo():string = "Enemy" ProcessCombat(Player:player_stats, Enemy:enemy_stats):void = using{Player} Print(GetInfo()) # Player.GetInfo() Print("{Mana}") # Player.Mana (no conflict) using{Enemy} # Now both are in scope Print("{Armor}") # Enemy.Armor (no conflict with Player) # ERROR: Conflicts must be qualified # Print(Health) # Ambiguous - both have Health # Print(GetInfo()) # Ambiguous - both have GetInfo # Must qualify conflicting members Print("{Player.Health}") Print("{Enemy.Health}") Print(Player.GetInfo()) Print(Enemy.GetInfo()) ``` When members exist in multiple `using` contexts, you must explicitly qualify to disambiguate. ### Mutable Member Local `using` works with mutable fields through the `set` keyword: ```verse config := class: var Volume:float = 1.0 var Quality:int = 2 UpdateSettings(Settings:config):void = using{Settings} # Mutable field access set Volume = 0.8 # Inferred as: set Settings.Volume = 0.8 set Quality = 3 # Inferred as: set Settings.Quality = 3 ``` ## Troubleshooting When working with modules, you may encounter various issues. Understanding these common problems and their solutions will help you debug module-related errors more efficiently. ### Module Not Found Errors **Problem**: The compiler reports that a module cannot be found when you try to import it. **Common Causes and Solutions**: 1. **Incorrect path**: Double-check the module path in your `using` statement. Remember that paths are case-sensitive. ```verse # Wrong: different case using { /verse.org/random } # Error: module not found # Correct: proper case using { /Verse.org/Random } # Works ``` 2. **Missing parent module import**: When importing nested modules, ensure the parent is imported first. ```verse # Wrong: child before parent using { Inventory } # Error if Inventory is nested # Correct: parent first using { GameSystems } using { Inventory } ``` 3. **File location mismatch**: Ensure your file structure matches your module structure. If you have a folder named `PlayerSystems`, all files in that folder are part of the `PlayerSystems` module. ### Access Denied Errors **Problem**: You can't access a member of an imported module. **Common Causes and Solutions**: 1. **Missing access specifier**: Members without the `` specifier are internal by default. ```verse # In ModuleA SecretValue:int = 42 # Internal by default PublicValue:int = 100 # Explicitly public # In another module using { ModuleA } X := ModuleA.SecretValue # Error: not accessible Y := ModuleA.PublicValue # Works ``` 2. **Protected or private members**: These are not accessible outside their defining scope. ```verse # In a class class_a := class: PrivateField:int = 10 ProtectedField:int = 20 PublicField:int = 30 # Outside the class Obj := class_a{} X := Obj.PrivateField # Error: private Y := Obj.PublicField # Works ``` ### Circular Dependency Errors **Problem**: Two modules try to import each other, creating a circular dependency. **Solution**: Restructure your code to avoid circular dependencies: 1. **Extract common code**: Move shared definitions to a third module that both can import. 2. **Use interfaces**: Define interfaces in a separate module to break the dependency cycle. 3. **Reconsider architecture**: Circular dependencies often indicate a design issue that needs rethinking. ### Name Collision Errors **Problem**: Two imported modules define members with the same name. **Solution**: Use fully qualified names to disambiguate: ```verse using { /GameA/Combat } using { /GameB/Combat } # Ambiguous Damage := CalculateDamage(10.0) # Error: which CalculateDamage? # Explicit DamageA := /GameA/Combat.CalculateDamage(10.0) # Clear DamageB := /GameB/Combat.CalculateDamage(10.0) # Clear ``` ### Persistence Issues **Problem**: Module-scoped variables aren't persisting as expected. **Common Causes and Solutions**: 1. **Wrong type used**: Ensure you're using `weak_map(player, t)` for player persistence. 2. **Type not persistable**: Check that your custom types have the `` specifier. 3. **Initialization timing**: Make sure you're initializing persistent data at the right time in the game lifecycle. ### Local Qualifier Conflicts **Problem**: Shadowing errors when local identifiers conflict with module members. **Solution**: Use the `(local:)` qualifier to disambiguate: ```verse ModuleX := module: Value:int = 10 ProcessValue((local:)Value:int):int = (ModuleX:)Value + (local:)Value # Clear distinction ```