Modules
A Mach project is a tree of modules rooted at the project's id. Each file is reachable by a dotted path; use imports a name privately and fwd re-exports it on a module's public surface.
Module paths
Every .mach file under a project's source directory is a module, reachable by a dotted path from the project root. The path separator is ., and each segment mirrors the directory and file name.
| Project id | File | Module path |
|---|---|---|
myproj | src/foo/bar.mach | myproj.foo.bar |
There is no this. self-prefix. Within a project, modules always reference each other by their full project-rooted path, so every reference reads the same regardless of where it appears.
Imports with use
use brings an external symbol or module into the current scope under a local name. It is a private import: the imported name is not exposed to consumers of this module.
use PATH; # binds the leaf component
use ALIAS: PATH; # binds ALIAS
- The alias defaults to the path's leaf component when omitted.
- One name per line. There is no splat (
use foo.*does not exist) and no combined form (use foo.{a, b, c}does not exist).
Module vs symbol binding
The resolver binds whatever the path points to. A path ending at a module binds the module, and you reach its members with qualified module.member access. A path ending at a symbol binds the symbol for bare use. Importing a module does not pull its members into scope unqualified: to use usize bare, import the symbol, not its module.
use std.types.size; # binds module 'size'; use as size.usize
use sz: std.types.size; # binds module under 'sz'; use as sz.usize
use std.types.size.usize; # binds symbol 'usize'; use bare as usize
use my_usize: std.types.size.usize; # binds symbol under 'my_usize'
A Mach module imports every dependency it directly names, including any reached only through a re-export. There is no shortcut for "my surface uses these transitively; just give me the leaf." The dependency graph stays visible at the top of every file.
Re-exports with fwd
fwd re-exports a symbol or module from another module under this module's public surface. It is the public counterpart to use, and it mirrors use's grammar exactly.
fwd PATH; # re-export under the path leaf
fwd ALIAS: PATH; # re-export with rename
fwdalways publishes; there is nopub fwdform.- One name per line. No splat.
use impl: full.core.data;
fwd impl.Point; # re-exports as 'Point'
fwd Pt: impl.Point; # re-exports as 'Pt'
A fwd path that ends at a module re-exports the whole module as a public module alias, mirroring use's module binding. A consumer reaches the alias's members with qualified access, chaining through any depth of re-export, including a fwd of another library's fwd.
fwd demo.alpha; # re-exports module 'alpha'
fwd deep: demo.deep.beta; # re-exports module under 'deep'
use demo.lib; # lib.mach contains: fwd demo.alpha;
lib.alpha.answer(); # resolves through the module re-export
As with use, a module alias is not a value; only its members can be named.
The shadow-module pattern
A file foo.mach may co-exist with a directory foo/. The file is the surface module, the public face of foo. The directory's files are split implementations that the surface loads and re-exports.
myproj/
foo.mach # surface
foo/
a.mach # split: myproj.foo.a
b.mach # split: myproj.foo.b
The surface loads each split with use and re-exports its public symbols with fwd. Consumers use myproj.foo and reach symbols through the surface; they never name the split files directly.
# myproj/foo.mach (surface)
use myproj.foo.a;
use myproj.foo.b;
fwd a.X;
fwd b.Y;
Two common uses:
- Topical splits - organize a large module by topic; all splits forwarded unconditionally.
- Multiplatform splits - one impl per target, selected by
$ifon$mach.build.osor$mach.build.arch, then aliased under a stable name for consumers.
Bare project-id imports
A one-segment use/fwd path equal to a resolvable project id (a dependency's id or the current project's own id) resolves to that project's declared [project].module, the surface file named by the manifest. A library glfw that declares module = "glfw.mach" is imported by its id alone.
use glfw; # binds glfw's declared [project].module
A bare import of a project that declares no module is an error, directing you to a full path or to add the manifest key. Longer paths are unaffected.
See also
- Visibility - what
pubexposes foruseandfwdto reach - Manifest - declaring
[project].modulefor bare imports - Project layout - how the source tree maps to module paths