Imports and modules (how-to)¶
This page shows how to structure multi-file projects and use imports in practice.
Prerequisite: follow Install, build, and run so you can run incan.
Contributor source-build fallback
If you are working from a compiler checkout instead of a toolchain install, you can run the repository-built binary directly:
- from the repository root:
./target/release/incan ...
- or via an absolute path (from anywhere):
/absolute/path/to/incan run path/to/file.incn
Simple multi-file project¶
Recommended structure:
myproject/
├── main.incn
├── models.incn
└── utils.incn
Example imports:
from models import User
import utils::format_currency
Nested projects¶
Recommended structure:
myproject/
└── src/
├── main.incn
├── db/
│ └── models.incn
└── shared/
└── utils.incn
Example imports:
from db.models import User
import shared::utils::format_date
Publish a structured library¶
Create the library with its public declarations in ordinary source modules:
hyperquant-lib/
├── incan.toml
└── src/
├── lib.incn
└── hyperquant/
├── index.incn
└── search.incn
# src/hyperquant/index.incn
pub model HyperquantIndex:
pub size: int
pub def build_index(size: int) -> HyperquantIndex:
return HyperquantIndex(size=size)
def pack_bits(size: int) -> int:
return size
# src/hyperquant/search.incn
from hyperquant.index import HyperquantIndex
pub def search(index: HyperquantIndex) -> int:
return index.size
Build the checked library artifact:
cd hyperquant-lib
incan build --lib
Add the library to the consumer:
[dependencies]
hyperquant_lib = { path = "../hyperquant-lib" }
Import the source-derived namespace:
from pub::hyperquant_lib import hyperquant
def main() -> None:
index = hyperquant.build_index(1024)
println(hyperquant.search(index))
No entry in src/lib.incn is needed merely to make hyperquant exist. Keep explicit pub from entries there when you also want a curated flat facade:
# src/lib.incn
pub from hyperquant.index import HyperquantIndex, build_index
When sibling modules publish the same declaration name, import through the exact child module instead of the parent:
from pub::codecs.encoding.base64 import encode
from pub::codecs.encoding.hex import encode as encode_hex
The compiler reports the conflicting source modules if you try the ambiguous parent import.
How module discovery works (practical view)¶
When you import a local module, the compiler:
- Resolves the path (handling
.,..,super,crate). - Looks for the
.incnfile (ormod.incnfor directories). - Parses and type-checks that file.
- Makes its types and functions available in your importing file.
Published-library consumers follow a different trust boundary: they resolve pub:: imports from the checked .incnlib artifact and generated library crate rather than re-reading the dependency's source.
Examples from the repo¶
- Multi-file example:
examples/advanced/multifile/
Run:
incan run examples/advanced/multifile/main.incn
- Nested project example:
examples/advanced/nested_project/
Run:
incan run examples/advanced/nested_project/src/main.incn
If you prefer browsing first, see the examples directory on GitHub: https://github.com/encero-systems/incan/tree/main/examples.
Share module-owned state across files¶
If a module needs to export live runtime state, use pub static:
This example shares live runtime state across module boundaries with pub static:
pub static hits: int = 0
pub def record_hit() -> None:
hits += 1
from counters import hits, record_hit
def main() -> None:
record_hit()
record_hit()
println(hits)
This prints 2.
For the full walkthrough, see: Module state (how-to).