Skip to main content

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 → Alia Null

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/false or uppercase TRUE/FALSE → Alia Boolean
  • Empty cells or null/NULL → Alia Null
  • 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 String type
  • 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 TypeSearch 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:

FieldTypeDescription
sourceStringThe original import path
aliasStringThe import alias
sourceDirStringThe source file directory
extStringThe file extension (e.g. .alia, .json, .csv)
resolvedPathStringThe resolved absolute file path
contentEncodedStringThe JSON-encoded file contents
contentStrStringThe raw file contents
exportsObject|nullThe 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 aliasRegistry and 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'
}
DeclarationMeaning
registLibRegisters 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
registDepRegisters a third-party dependency
declareLibAliasDeclares that an import name is served by a host library, so no backend has to hard-code library names
declareLibDepDeclares whether a dependency is emitted based on whether its host library is used
declareLibInlineDeclares 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​