Module System
Alia's module system supports code organization and data import; the import and export statements enable code reuse and data sharing between modules.
1. Importing Modules
Basic Syntax
import <String modulePath> [as <Key alias>]
Importing Alia Modules
~ import a module with an alias
import 'utils.alia' as utils
print utils.format 'Hello'
~ direct import (module contents are merged into the current scope)
import 'math.alia'
print add 1 and 2
Importing Data Files
Alia supports importing JSON and CSV files directly, automatically converting them to the corresponding built-in types.
Importing JSON Files
JSON files are automatically converted to the Object type:
~ contents of config.json:
~ { "name": "Alia", "version": 0.1, "features": ["type-safe", "extensible"] }
import 'config.json' as config
print Object.get config keyed 'name' ~ outputs 'Alia'
print Object.get config keyed 'version' ~ outputs 0.1
Conversion rules:
- JSON object → Alia
Object - JSON array → Alia
Array - JSON string → Alia
String - JSON number → Alia
Number - JSON boolean → Alia
Boolean - JSON
null→ AliaNull
Importing CSV Files
CSV files are automatically converted to the Sheet type, with the first row as the header:
~ contents of data.csv:
~ name,age,city,score
~ Alice,30,Beijing,95
~ Bob,25,Shanghai,88
import 'data.csv' as sheet
print sheet.getCell at row 0 column 'name' ~ outputs 'Alice'
print sheet.getCell at row 1 column 'age' ~ outputs 25
print sheet.headers ~ outputs name,age,city,score
Conversion rules:
- CSV first row → Sheet headers (
headers) - CSV subsequent rows → Sheet data rows (
data) - Numeric strings → Alia
Number - Lowercase
true/falseor uppercaseTRUE/FALSE→ AliaBoolean - Empty cells or
null/NULL→ AliaNull - Quoted fields → fields containing commas are supported
Importing Text Files
Any file whose extension is not .json, .csv, or .alia is automatically converted to the String type:
~ import an HTML template
import 'template.html' as template
print template
~ import a configuration file
import 'settings.ini' as settings
print settings.includes 'debug'
~ import a Markdown file
import 'readme.md' as content
print content.split '\n'
Conversion rules:
- File contents → Alia
Stringtype - Original line breaks and whitespace are preserved
- All String methods are available (such as
split,slice,replace, etc.)
Module Aliases
Use the as keyword to give an imported module an alias:
import 'long-module-name.alia' as m
print m.doSomething
2. Exporting Modules
Basic Syntax
export <Value exportValue>
Default Export
Exports all content of the current module context:
~ utils.alia
const format = alias <String str> -> String {
'[ ' .. str .. ' ]'
}
const add = alias <Number a> and <Number b> -> Number {
a + b
}
export this
Selective Export
Exports specific functions or objects:
~ math.alia
const PI = 3.14159
const E = 2.71828
export *[ PI: PI, E: E ]
Exporting a Single Value
~ constants.alia
const MAX_VALUE = 1000
export *[ MAX_VALUE: MAX_VALUE ]
export <Value> directly uses the expression's evaluated result as the module's default export; exporting named bindings requires the export *[...] object form.
3. Module Paths
Standard Library Paths
Built-in standard library names (args, ffi, fs, io, process, web) without a directory prefix load the corresponding built-in implementations directly, bypassing file path resolution:
import 'fs' as fs
import 'process' as process
Relative Paths
Paths relative to the current file:
import './utils.alia' as utils
import '../config.json' as config
Project Paths
Inside a project (a directory containing main.alia), imports without a directory prefix are resolved automatically according to file type:
| File Type | Search Path |
|---|---|
.json | <project root>/assets/xxx.json |
.csv | <project root>/assets/xxx.csv |
.alia | <project root>/libs/xxx.alia |
| Other extensions | <project root>/assets/xxx.<ext> |
| No extension | <project root>/libs/xxx.alia |
Module-space package resolution: when the target module does not exist under libs/, the system continues searching in
<project root>/mods/<package>@<version>/lib.alia (the version comes from the dependencies in config.json,
falling back to a name-matched directory under mods/ when missing):
~ installed module (alia add gtk-alia installs to mods/gtk-alia@0.1.0/)
import 'gtk-alia' as gtk
~ in a project, JSON files are found automatically under assets/
import 'config.json' as config
~ in a project, Alia modules are found automatically under libs/
import 'utils' as utils
Standalone File/Code Paths
Outside a project, imports without a directory prefix are resolved relative to the current execution directory:
~ in a standalone file, relative to the current execution directory
import 'data.json' as data
Extension Rules
.alia: Alia source code files.json: JSON data files (automatically converted to Object).csv: CSV data files (automatically converted to Sheet)- Other extensions: text files (automatically converted to String)
If the extension is omitted, .alia is appended by default:
import 'utils' ~ equivalent to import 'utils.alia'
4. Module Caching
Module imports use a caching mechanism: the same module is loaded only once within the same runtime environment:
~ first import: executes and caches
import 'cache.alia' as c1
~ second import: returns the cache directly
import 'cache.alia' as c2
~ c1 and c2 point to the same module instance
5. Usage Examples
Example 1: Configuration Management
~ config.json
~ {
~ "server": "localhost",
~ "port": 8080,
~ "debug": true
~ }
import 'config.json' as config
print ('Server: ' .. (Object.get config keyed 'server'))
print ('Port: ' .. (Object.get config keyed 'port'))
if (Object.get config keyed 'debug').valueOf {
print 'Debug mode enabled'
}
Example 2: Data Processing
~ sales.csv
~ product,region,amount
~ Laptop,North,1000
~ Phone,South,800
~ Laptop,South,1200
import 'sales.csv' as salesData
~ read an entire column
const products = salesData.getColumn 'product'
print products
~ read a cell
print salesData.getCell at row 0 column 'amount'
print salesData.getCell at row 2 column 'region'
Example 3: Module Composition
~ constants.alia
const PI = 3.14159
export *[ PI: PI ]
~ calculator.alia
import 'constants.alia'
const circleArea = alias <Number radius> -> Number {
PI * radius * radius
}
export *[ circleArea: circleArea ]
~ main.alia
import 'calculator.alia' as calc
print 'Circle area: ' .. (calc.circleArea 5)
Example 4: Text File Import
~ contents of template.html:
~ <div class="container">
~ <h1>Hello, {{name}}!</h1>
~ <p>Welcome to Alia.</p>
~ </div>
import 'template.html' as template
~ get the template contents
print template
~ process line by line
const lines = template.split '\n'
print 'Template has ' .. lines.length .. ' lines'
~ look for the placeholder
if template.includes '{{name}}' {
print 'Template contains placeholder'
}
6. The Compilation Process of Imports
In compilation mode, import statements are handled by the @compileNode "ImportStatement" override in the backend (e.g. js.alia).
The importData Structure at Compile Time
The compiler builds an importData object for each ImportStatement node with the following fields:
| Field | Type | Description |
|---|---|---|
source | String | The original import path |
alias | String | The import alias |
sourceDir | String | The source file directory |
ext | String | The file extension (e.g. .alia, .json, .csv) |
resolvedPath | String | The resolved absolute file path |
contentEncoded | String | The JSON-encoded file contents |
contentStr | String | The raw file contents |
exports | Object|null | The Alia file's export information ({ type, names }) |
Customizing Import Compilation
You can override the ImportStatement node in a backend to customize import behavior:
@compileNode "ImportStatement" -> js {
let data = _bound.importData
let safeAlias = safeObjectName data.alias
if data.ext == '.alia' {
~ recursively compile the Alia module with @compile
let content = readFile data.resolvedPath
let compiled = @compile "js" content (dirname data.resolvedPath)
~ wrap the export result
let exportNames = []
if data.exports & data.exports.names {
exportNames = data.exports.names
}
if exportNames.length > 0 {
let names = ''
let i = 0
while i < exportNames.length {
if i > 0 { names = names .. ', ' }
names = names .. exportNames[i]
i = i + 1
}
compiled = '(function() {\n' .. compiled .. '\nreturn { ' .. names .. ' };\n})()'
} else {
compiled = 'wrapObject(' .. compiled .. ')'
}
return 'const ' .. safeAlias .. ' = ' .. compiled
}
~ JSON/CSV/text files use the preprocessed contents
return 'const ' .. safeAlias .. ' = ' .. data.contentEncoded
}
Recursive Compilation with @compile
The @compile directive compiles arbitrary Alia code snippets inside the compilation entry or overrides:
@compile <Expression langName> <Expression code> [<Expression sourceDir>]
- Shares the current compiler's
aliasRegistryand override rules - Has an independent variable scope
- Returns the compiled target language code string
7. Declaring Library Runtimes and Dependency Ownership
A library (standard library or third-party module) registers itself into a target language's compile entry via @compileWith <lang>. Base backends hard-code no library names — every ownership relation is declared by the library itself, so adding a library requires no compiler or standard-library change:
@compileWith nodejs {
import './runtime.js' as myRuntime
import './web-runtime.js' as myWebRuntime
registLib 'mylib' with myRuntime as '__aliaMyLib'
registDep 'my-native-dep' at '^1.0.0'
~ other import names served by the same runtime all belong to the host library
declareLibAlias 'mylib-web' with 'mylib'
~ whether this npm dependency is emitted depends on whether the host library is used
declareLibDep 'my-native-dep' with 'mylib'
~ this runtime's file must be emitted into the artifacts for targets without a package manager
declareLibInline 'mylib' with 'web'
}
| Declaration | Meaning |
|---|---|
registLib | Registers the library's runtime artifact for this language; it only reaches the artifacts when the library (or one of its aliases) is imported by the entry |
registDep | Registers a third-party dependency |
declareLibAlias | Declares that an import name is served by a host library, so no backend has to hard-code library names |
declareLibDep | Declares whether a dependency is emitted based on whether its host library is used |
declareLibInline | Declares that the library's runtime file must be emitted into the artifacts for the given target (e.g. inlining where the target has no package manager to install third-party runtimes) |
These helpers are defined by each backend in its @compileEntry (e.g. Base/Core/src/backends/nodejs.alia); see the compiler documentation for their semantics.
Next Steps
- Null Safety
- Object System - Working with the Object type after JSON imports
- Sheet 2D Table Class - Working with the Sheet type after CSV imports
- String Class - Working with the String type after text imports