Module System
Grapa provides a sophisticated module system that combines compile-time includes with runtime dynamic class loading and automatic file loading. This system is more flexible and powerful than traditional import/export mechanisms.
Overview
Grapa's module system consists of three main components:
- Include System - Compile-time file inclusion
- Dynamic Class Loading - Runtime class resolution with search paths
- Automatic File Loading - Automatic loading of
.grc
files for function calls - Manual File Loading - Manual file reading and execution using
$file()
commands
Automatic File Loading
Important: This is runtime loading - files are loaded when the function or class is first called, not at compile time. This differs from the include
command which loads files at compile time.
Function Call Auto-Loading
When you call a function that doesn't exist in the current namespace, Grapa automatically searches for and loads the corresponding .grc
file:
/* File: lib/grapa/mycode.grc */
$global["mycode"] = class {
test = op() {
"Hello from mycode.grc!".echo();
return "Success from mycode.grc";
};
};
/* Usage - automatically loads mycode.grc */
x = mycode(); /* Searches for mycode.grc and loads it */
x.test(); /* Calls the test method */
Class Assignment Auto-Loading
When you assign a class that doesn't exist, Grapa searches for a .grc
file with the same name and looks for a class definition:
/* File: myclass.grc */
$global["myclass"] = class {
message = "Hello from myclass!";
greet = op() {
message.echo();
};
};
/* Usage - automatically loads myclass.grc */
x = myclass(); /* Searches for myclass.grc and loads the myclass class */
x.greet(); /* Calls the greet method */
Important: The filename must match the class name. If myclass.grc
doesn't define a class named myclass
, the assignment will fail.
Function Return Value Auto-Loading
If a .grc
file doesn't define a class with the matching name, it can still be loaded as a function that returns a value:
/* File: utils.grc */
utils = op() {
return "Utility functions loaded";
};
/* Usage - automatically loads utils.grc */
result = utils(); /* Searches for utils.grc and calls the utils function */
result.echo(); /* Outputs: "Utility functions loaded" */
Function-Only Files (Re-loading Behavior)
Files that only define functions (without defining $global["filename"]
) will re-load every time they're called:
/* File: $editor.grc - Example from lib/grapa/$editor.grc */
$global.createEditor = op(label) {
/* Editor creation logic */
};
$global.newEditor = op(label) {
/* Editor initialization logic */
};
/* No $global["$editor"] definition - will re-load every time */
/* Usage - re-loads every time */
$editor(); /* Re-loads and executes $editor.grc */
$editor(); /* Re-loads and executes $editor.grc again */
Important: To prevent re-loading, define $global["filename"]
in your file:
/* File: utils.grc - Cached version */
$global.utils = op() {
return "Utility functions loaded";
};
/* Define the global to prevent re-loading */
$global["utils"] = $global.utils;
/* Usage - loads once, then cached */
result = utils(); /* Loads utils.grc and caches the function */
result = utils(); /* Uses cached version, doesn't re-load */
Search Paths for Auto-Loading
The automatic file loading mechanism searches in this order:
- Current Working Directory (
gSystem->mPath
) - Where you run thegrapa
command - Library Directory (
gSystem->mLibDir
) - Default:{current_directory}/lib/grapa
- Static Library (
gSystem->mStaticLib
) - Built-in classes
Default Library Directory Search Order
The library directory is determined in this order:
{current_working_directory}/lib/grapa
(development/local)/usr/lib/grapa
(system-wide, package manager)/usr/local/lib/grapa
(system-wide, user-installed){binary_directory}/lib/grapa
(fallback, relative to grapa executable)
Note: The current implementation prioritizes development convenience by checking the current working directory first. This makes it easy for developers to place library files in their project's
lib/grapa/
directory.
Runtime vs Compile-Time Loading
Grapa provides three different mechanisms for loading code:
Automatic File Loading (Runtime)
- When: Files are loaded when functions/classes are first called
- How: Searches for
.grc
/.grz
files in search paths - Use case: Dynamic loading, plugins, optional features
- Example:
mycode()
automatically loadsmycode.grc
when called
Include Command (Compile-Time)
- When: Files are loaded during compilation/parsing
- How: Explicitly includes files with
include "file.grc"
- Use case: Core dependencies, always-required modules
- Example:
include "lib/core/utils.grc"
loads file during compilation
Manual File Loading (Runtime)
- When: Files are loaded manually using
$file()
commands - How: Read file content and execute with
op()()
orexec()
- Use case: Dynamic file loading from any location, custom loading logic
- Example:
$file().read("path/to/file.grc").exec()
loads and executes file content
Include System
Basic Include Syntax
include "lib/grapa/grapa.grc";
include "lib/custom/string_utils.grz";
File Types
Source Files (.grc)
/* Development - parsed and compiled during compilation */
include "lib/custom/string_utils.grc";
Characteristics: - Parsed and compiled during compilation - Best for development and debugging - Full syntax checking and optimization - Slower compilation time
Pre-compiled Files (.grz)
/* Production - loaded directly for speed */
include "lib/custom/string_utils.grz";
Characteristics:
- Loaded directly without parsing
- Best for production deployment
- Fast compilation time
- Created using $sys().compilef()
Creating Pre-compiled Files
/* Compile a source file to pre-compiled format */
$sys().compilef("lib/custom/string_utils.grc", "lib/custom/string_utils.grz");
Dynamic Class Loading
When you reference an undefined class, Grapa automatically searches for and loads class definitions.
Automatic Class Loading
/* Class will be automatically loaded from search paths */
custom = $CUSTOM_CLASS();
result = custom.some_method();
Search Path Management
$GRAPA_PATH Environment Variable
The $GRAPA_PATH
variable contains a queue of search locations:
/* Set multiple search paths */
$sys().putenv($GRAPA_PATH, [
"lib/custom", /* Custom libraries */
"lib/extensions", /* Extensions */
"lib/plugins", /* Plugins */
"/usr/local/lib/grapa" /* System libraries */
]);
/* Add to existing path */
current_path = $sys().getenv($GRAPA_PATH);
new_path = current_path + ["lib/new"];
$sys().putenv($GRAPA_PATH, new_path);
Search Path Modification Command
You can modify the search paths using the $sys().putenv()
command:
/* Add a new search path */
$sys().putenv($GRAPA_PATH, $sys().getenv($GRAPA_PATH) + ["lib/new_feature"]);
/* Replace all search paths */
$sys().putenv($GRAPA_PATH, ["lib/prod", "lib/stable"]);
/* Remove a specific path */
current_paths = $sys().getenv($GRAPA_PATH);
filtered_paths = current_paths.filter(op(path) { path != "lib/old_feature"; });
$sys().putenv($GRAPA_PATH, filtered_paths);
Note: The automatic file loading mechanism uses these search paths, but there's no way to specify a custom location for individual file loads. For custom file locations, use manual file loading instead.
$GRAPA_LIB Environment Variable
The $GRAPA_LIB
variable points to the main library directory:
/* Set the main library directory */
$sys().putenv($GRAPA_LIB, "/usr/local/lib/grapa");
/* Get current library directory */
lib_dir = $sys().getenv($GRAPA_LIB);
Search Order
When loading a class, Grapa searches in this order:
- Current namespace - Check if class already exists
- $GRAPA_PATH locations - Search each path in the queue
- $GRAPA_LIB directory - Main library directory
- Static library - Built-in classes
Practical Examples
Automatic Function Loading
/* Create a utility function */
/* File: lib/grapa/utils.grc */
$global["utils"] = class {
format_date = op(date) {
/* Date formatting logic */
return date.format("YYYY-MM-DD");
};
validate_email = op(email) {
/* Email validation logic */
return email.grep(r"^[^@]+@[^@]+\.[^@]+$", "x").len() > 0;
};
};
/* Usage - automatically loads utils.grc */
formatted = utils().format_date($TIME());
is_valid = utils().validate_email("user@example.com");
Custom Class Library
/* Create custom class */
/* File: lib/custom/math_utils.grc */
$global["$MATH_UTILS"] = class {
add = op(x, y) { x + y; };
multiply = op(x, y) { x * y; };
factorial = op(n) {
if (n <= 1) 1 else n * factorial(n - 1);
};
};
Using Custom Classes
/* Set up search path */
$sys().putenv($GRAPA_PATH, ["lib/custom"]);
/* Class will be automatically loaded */
math = $MATH_UTILS();
result = math.add(5, 3); /* 8 */
fact = math.factorial(5); /* 120 */
Plugin System
/* Dynamic plugin loading */
plugin_paths = [
"plugins/network",
"plugins/database",
"plugins/ui"
];
$sys().putenv($GRAPA_PATH, plugin_paths);
/* Plugins are loaded on demand */
network = $NETWORK_PLUGIN();
db = $DATABASE_PLUGIN();
ui = $UI_PLUGIN();
Development vs Production Setup
Development Environment
/* Use .grc files for easy debugging */
$sys().putenv($GRAPA_PATH, [
"lib/dev",
"lib/custom"
]);
Production Environment
/* Use .grz files for performance */
$sys().putenv($GRAPA_PATH, [
"lib/prod/optimized",
"lib/prod/stable"
]);
$sys().putenv($GRAPA_LIB, "/opt/grapa/lib");
Manual File Loading
For cases where you need to load files from specific locations or implement custom loading logic, you can use manual file loading with $file()
commands.
Basic Manual File Loading
/* Read and execute a file from any location */
file_content = $file().read("/path/to/custom/file.grc");
result = file_content.exec();
/* Load and execute with error handling */
load_file = op(file_path) {
if ($file().exists(file_path)) {
content = $file().read(file_path);
return content.exec();
} else {
return {"error": "File not found: " + file_path};
};
};
/* Usage */
result = load_file("/custom/path/utils.grc");
Dynamic File Loading
/* Load files based on configuration */
config = {
"dev_mode": true,
"custom_paths": ["/dev/lib", "/prod/lib"]
};
load_from_config = op() {
if (config.dev_mode) {
/* Load development files */
dev_content = $file().read(config.custom_paths[0] + "/dev_utils.grc");
return dev_content.exec();
} else {
/* Load production files */
prod_content = $file().read(config.custom_paths[1] + "/prod_utils.grc");
return prod_content.exec();
};
};
File Loading with Parameters
/* Load and execute with parameters */
load_with_params = op(file_path, params) {
content = $file().read(file_path);
/* Create a function that accepts parameters */
func = op()(content)();
/* Call with parameters */
return func(params);
};
/* Usage */
result = load_with_params("/path/to/script.grc", {"name": "Alice", "age": 30});
Comparison of Loading Methods
Method | When to Use | Advantages | Limitations |
---|---|---|---|
Include | Compile-time dependencies | Fast, always available | Fixed at compile time |
Automatic | Runtime dynamic loading | Convenient, automatic discovery | Limited to search paths |
Manual | Custom locations, dynamic logic | Full control, any location | Requires explicit code |
Advanced Usage
Dynamic Path Management
/* Add paths dynamically */
add_search_path = op(new_path) {
current = $sys().getenv($GRAPA_PATH);
updated = current + [new_path];
$sys().putenv($GRAPA_PATH, updated);
};
add_search_path("lib/new_feature");
Conditional Loading
/* Load different libraries based on environment */
if ($sys().getenv("DEBUG_MODE") == "true") {
$sys().putenv($GRAPA_PATH, ["lib/debug", "lib/dev"]);
} else {
$sys().putenv($GRAPA_PATH, ["lib/prod"]);
}
Library Organization
/* Organize libraries by category */
$sys().putenv($GRAPA_PATH, [
"lib/core", /* Core functionality */
"lib/extensions", /* Extensions */
"lib/plugins", /* Plugins */
"lib/experimental" /* Experimental features */
]);
Performance Considerations
Caching
- Classes are cached after first load
- Subsequent references use cached version
- No re-parsing or re-compilation needed
Function Caching Behavior
Files with $global["filename"]
definition:
- Loaded once and cached
- Subsequent calls use cached version
- Best performance for frequently used functions
Files without $global["filename"]
definition:
- Re-loaded every time they're called
- Each call re-executes the entire file
- Suitable for initialization scripts or one-time setup
Performance Examples
/* Cached function (good performance) */
/* File: cached_utils.grc */
$global.cached_utils = op() { return "cached"; };
$global["cached_utils"] = $global.cached_utils;
/* Non-cached function (re-loads every time) */
/* File: uncached_utils.grc */
$global.uncached_utils = op() { return "uncached"; };
/* No $global["uncached_utils"] definition */
/* Usage */
cached_utils(); /* Loads once, then cached */
cached_utils(); /* Uses cache - fast */
uncached_utils(); /* Loads and executes file */
uncached_utils(); /* Loads and executes file again - slower */
File Type Selection
- Development: Use
.grc
files for debugging - Production: Use
.grz
files for speed - Mixed: Use
.grz
for stable libraries,.grc
for active development
Search Path Optimization
/* Optimize search order - most used first */
$sys().putenv($GRAPA_PATH, [
"lib/frequently_used", /* Most common libraries */
"lib/standard", /* Standard libraries */
"lib/rare" /* Rarely used libraries */
]);
Error Handling
Missing Classes
If a class cannot be found:
1. Search through all $GRAPA_PATH
locations
2. Check $GRAPA_LIB
directory
3. Check static library
4. Return error if not found
File Loading Errors
- Invalid
.grz
files cause decompression errors - Syntax errors in
.grc
files cause parsing errors - Missing files are handled gracefully
Integration with Include System
The include system and class loading work together:
/* Include system - compile time */
include "lib/core/grapa.grc";
/* Class loading - runtime */
custom = $CUSTOM_CLASS(); /* Searches $GRAPA_PATH automatically */
/* Automatic file loading - runtime */
utils = utils(); /* Automatically loads utils.grc */
Best Practices
- Use automatic file loading for simple utilities and functions
- Use $GRAPA_PATH for custom libraries and plugins
- Use $GRAPA_LIB for system-wide library directory
- Use .grc files during development
- Use .grz files in production
- Organize libraries by category
- Optimize search path order
- Cache frequently used libraries
Comparison with Traditional Modules
Grapa's module system is more flexible than traditional import/export:
Traditional Modules | Grapa Module System |
---|---|
Static imports | Dynamic class loading + automatic file loading |
Fixed dependencies | Runtime path configuration |
Compile-time resolution | Runtime resolution |
Limited flexibility | High flexibility |
Single search location | Multiple search paths |
Explicit imports | Automatic discovery |
Conclusion
Grapa's module system provides:
- Compile-time includes for code organization
- Runtime class loading with flexible search paths
- Automatic file loading for seamless function calls
- Manual file loading for custom locations and dynamic logic
- Environment variable management for configuration
- Performance optimization with pre-compiled files
- Extensible architecture for custom search providers
This system is more sophisticated than traditional import/export modules and provides greater flexibility for dynamic loading and configuration.
Key Behaviors
- Filename Matching: For automatic loading, the filename must match the class name (e.g.,
myclass.grc
must define$global["myclass"]
) - Class vs Function: Files can define either classes (for assignment) or functions (for function calls)
- Search Paths: Automatic loading uses predefined search paths that can be modified with
$sys().putenv($GRAPA_PATH, ...)
- Manual Control: For files outside search paths, use
$file().read()
and manual execution - Caching Behavior: Files with
$global["filename"]
are cached; files without it re-load every time
Related Documentation
- Command Operators - Include syntax details
- System Functions - Environment variable management
- Object Methods - Class and method usage