v5 Modules: imports, exports, and a linker mindset
Lesson, slides, and applied problem sets.
View SlidesLesson
v5 Modules: imports, exports, and a linker mindset
Why modules are the scale point
Before modules, names live in one mostly global pool. With modules, names are partitioned by explicit ownership and visibility. That enables:
- composition without collisions
- clean APIs (
export) - deterministic linking
1) Public surface vs private body
module NAME { ... } defines a unit with two parts:
- public surface: exported declarations
- private implementation: non-exported declarations
Private names intentionally cannot be accessed outside module scope.
This is not just style; it changes the language contract and analyzer obligations.
2) Import forms and alias model
Supported forms:
import math;import math as alias;
Alias rules:
- explicit alias replaces default identifier
- alias lives in importing module scope as immutable binding in this pack
- duplicates in same scope are errors
An import does not clone symbols; it creates a module handle and visibility edge.
3) Qualified access contract
Only module access form is valid:
alias.name
This requires:
- alias resolution
- module existence
- export membership of
name
If name is not exported, runtime never sees it through this module contract.
math.add is parsed as member-like access, then resolved to map/index semantics by compiler.
4) Entry module rule
main is the entry module and must be final.
This keeps linking simple in this stage:
- dependency checks only need one pass
- initialization order is deterministic
- final execution entry is stable
5) Why modules and linker are coupled
Without stable module boundaries, linker cannot:
- know import graph
- patch function closures deterministically
- compose
MAINsections in correct order
This is why module ordering, exports, and imports are enforced together.
6) Common pitfalls
- relying on hidden globals across module boundaries
- using non-exported symbol via alias
- importing a module declared later (forward import)
- duplicate module names
- assuming import aliases carry runtime-only behavior
Each has a clear static diagnostic in this pack.
7) Practice checkpoints (almost solved)
1) Entry rule + module main {} in middle and another module later should fail.
2) Unknown module import + module main { import missing; } should fail with unknown module diagnostic.
3) Export requirement + module util { let hidden = 1; export let public = 2; } then main accessing util.hidden should fail; util.public should pass.
4) Alias reuse + import math as m; import util as m; should fail as redeclare collision.
5) default alias + import util; binds alias util.
6) Map lowerings + util.add lowers to module map lookup in compiled form, preserving no new VM opcode requirement.
Module compilation is where language structure becomes API contract.
Module Items
Program Lexer v5