Skip to content

Modules and Packages

This page defines source modules, imports, namespaces, packages, and public interfaces. See the Modules guide for common forms.

Source Module

Each .dq file is a module. Its canonical identity comes from its package or module root and its relative path, not merely its basename. Two source paths must not resolve to the same canonical module identity with different contents.

Declarations before implementation make up the module's public interface. Declarations after it are private implementation details or definitions of earlier public declarations.

Basic Imports

use imports a module:

use print
use ./helpers

By default, eligible public symbols merge into unqualified lookup and the module also receives a namespace. Namespace access uses @name.Symbol.

Aliases and Merge Control

as chooses a namespace alias. -- suppresses unqualified scope merging.

use ./math_extra as math --
var value : int = @math.Add(1, 2)

only(...) merges only named interface symbols. exclude(...) merges all eligible symbols except those named. --, only, and exclude are mutually exclusive merge selectors.

Importing the same canonical module more than once does not create another runtime module instance. Reusing aliases or merge selections must remain consistent and unambiguous.

Reexports

reexport makes imported public symbols part of the importing module's public interface. Selection and alias rules apply before reexporting.

use ./leaf only(Value, Run) reexport

A facade module can therefore expose a stable interface without duplicating declarations. Reexport conflicts are diagnosed rather than silently shadowed.

Relative and Root Paths

use ./child
use ../sibling
use ^/root_child

./ starts at the importing module's directory, ../ moves to its parent, and ^/ starts at the established module/package root. A path that escapes above that root is invalid.

Bare imports are resolved through package search roots. The chosen package directory provides the root from which canonical module names and relative imports are derived.

The compiler searches built-in standard/runtime roots, installation roots, the user package directory, and explicit --pkg-path roots. Later explicit roots have higher precedence, allowing a project to override installed packages.

Built-in Search Roots

The compiler adds these package search roots automatically. <compiler-dir> is the directory containing the dq-comp executable:

/usr/lib/dq/stdpkg
<compiler-dir>/../lib/dq/stdpkg
<compiler-dir>/../stdpkg
/usr/lib/dq/packages
<compiler-dir>/../lib/dq/packages
$HOME/.dq/packages

Package roots are checked from last to first. Therefore, a matching package in $HOME/.dq/packages takes precedence over the installation roots, and a root provided with --pkg-path takes precedence over all built-in roots. If several --pkg-path options are given, the last one takes precedence.

A package is source-first: its directory maps a package name to a module tree. The root module conventionally has the same name as the package. Optional project/build metadata does not change the language identity of a module.

Namespace and Symbol Conflicts

An unqualified symbol import must not make lookup ambiguous. Local declarations, multiple merged modules, aliases, selected imports, and reexports are checked for conflicts. Qualification is the resolution mechanism; import order does not silently choose one of two incompatible declarations.

@. explicitly selects the current module's global scope. Inside object methods, this is also the way to bypass member-first lookup without merging a namespace.

Interface Loading and Cycles

The compiler serializes public semantic information to standalone .dqm_if files and emits code to paired .o files. This is an implementation detail of incremental compilation; it does not change source-level visibility.

Circular module references are accepted only when their public interfaces can be formed without requiring private implementation state. A cycle whose interface cannot be resolved is a compile error with the dependency chain.

Initialization

An imported module is initialized before code that depends on it executes. Module initialization follows dependency order and runs once for each canonical module. Finalization, where supported, occurs in the corresponding reverse order.

Method-Local Use

Object methods may opt into module-scope value lookup with use . or a module namespace already available to the surrounding module. This restricted form does not load a new source module from an arbitrary method body.