Modules and packages
A module is one .bq file. A package is a directory with buraaq.pkg. That is the whole module system for the programs you write on day one.
You do not write mod.rs. You do not write __init__.py. You do not write a CMake graph so two files can see each other. The path is the name.
This is how Buraaq stays Python-shaped: the default path still emits one native binary. Optional scripting compiles the same language through clang for a fast edit loop. The files are just how you think.
Why this beats the usual tax
| What other stacks make you do | What Buraaq does |
|---|---|
| C / C++: headers, include guards, a build file | File under src/ is enough |
Python: __init__.py, sys.path, a venv | buraaq.pkg + a binary |
Rust: mod declarations, use crate:… | Path is the module; use sibling.item |
| Go: package clause must match the directory | Optional module line, must match the path |
JS: import paths, bundlers, node_modules | Unique std.* names import themselves |
The compiler frontend that performs this resolution is itself written in Buraaq (lexer, parser, names, MIR, LLVM text). Install covers winget, the Release zip, and the clone scripts.
Path is the module name
| File | Module |
|---|---|
src/main.bq | main |
src/tx.bq | tx |
src/account.bq | account |
src/http/server.bq | http.server |
src/lib.bq | lib |
Optional:
module account
If you write that line, it must match the path or the compile fails. Most files omit it.
Auto-import: stay in the program
In a project, unique stdlib names import themselves. If only one stdlib module exports println, you do not write use. Drop the std. prefix when you do: use io, use keel.
fn main() {
name = "Ada"
println("Ledger for {name}")
println(5000)
}
That is a complete program. println comes from std.io (text, int, float, or bool). page, api, and run come from std.keel. hold, stow, zeros, dot: same rule.
Auto-import does not pull in your own modules. A name that exists in two stdlib modules is not unique (connect, show, keep); write use db or use net. A name you defined locally wins over std. std.service is keel, so page / run auto-import.
You still write use when you want it explicit:
use io
use keel.{page, api, run}
use std.io.println
Ceremony is optional. The program is not.
Your files: use the sibling
Stdlib can auto-import. Your modules cannot: that is deliberate. You name the dependency so the graph stays visible.
src/tx.bq:
pub struct Tx {
amount: int
note: text
}
pub fn signed(amount: int, note: text) -> Tx {
Tx { amount: amount, note: note }
}
src/main.bq:
use tx.{Tx, signed}
fn main() {
t = signed(5000, "payroll")
println(t.note)
println(t.amount)
}
buraaq run
Default visibility is private to the file. Export with pub or the other module cannot see it.
pub fn listen(addr: text) -> Server {
...
}
fn helper() {
...
}
use tx.{Tx, signed} only works for pub items.
A complete two-module compile
This is the shape the compiler tests: two files, one package, one native binary.
src/util.bq:
pub fn add(a: int, b: int) -> int {
a + b
}
src/main.bq:
use util.add
fn main() {
print(add(2, 3))
}
No extra config. buraaq.pkg already named the package when you ran buraaq new.
Nested directories
src/http/server.bq → module http.server
use http.server.listen
The dots follow the folders. That is the whole convention.
Cycles are errors, not mysteries
error: cyclic module dependency
main → account → tx → account
Break the cycle by putting shared types in a third file (src/types.bq) that both import. The compiler prints the cycle. It does not guess.
buraaq.pkg
[package]
name = "ledger"
version = "1.0.0"
entry = "main"
type = "bin"
That is the only config file for a normal app. Stdlib (std.*) is always available. You do not add it as a dependency.
buraaq.lock pins versions when you have external packages. You do not need a hosted registry to write a ledger.
Packages you can fetch today
The public index is the repo tree packages/index.json (example: {"hello":"0.1.0"}).
buraaq add hello
buraaq index
add copies packages/hello/0.1.0/pkg.bq into .buraaq/cache. index serves that tree on :7423. buraaq fetch URL copies a file:// or HTTP pkg.bq. Set BURAAQ_REGISTRY if you point at another tree. Hosted packages.buraaq.dev is next, not a 1.0 requirement.
Tests live beside the code
tests/smoke.bq
Or a test "…" { expect … } block in src/. See Testing.
What you never write
- A second language to glue files together
- Lifetime parameters so module A can mention a type from module B
- A Docker build stage so the modules exist on a server
The package compiles to one native executable. Ship hashes that executable. Modules are how you think. The binary is what the OS runs.
Next: take the banking CLI you have been extending and split it, then serve it.
