Modules
Files are modules
Section titled “Files are modules”One file is one module; the directory layout is the module path.
geometry/vec.pp is the module geometry.vec. There is no module
declaration inside a file.
Imports
Section titled “Imports”import geometry.vec.{ Vec2, dot } // named imports (braced list)import geometry.vec.{ dot as vdot } // name import with renameimport geometry.vec.Vec2 // one name, same as .{ Vec2 }import geometry.vec // the whole module, used qualifiedimport geometry.vec as g // module import with a custom qualifierThree forms:
import path.{ Name, ... }brings the listed names into scope bare (trailing comma allowed).Name as Localrenames a name on import:import m.{ helper as h }makeshavailable;helperis not in scope.import path.Namebrings the one trailing name into scope, exactly likeimport path.{ Name }.import pathimports the module itself. Its exports are used qualified by the path’s last segment:vec.dot(a, b), a type positionvec.Vec2, a record literalvec.Vec2 { x: 1.0 }, a static callvec.Vec2.new(...), a variant literalvec.Shape.Circle { r: 1.0 }, and a match patternvec.Shape.Circle { r } =>(a pattern’s qualifier is dropped; the variant resolves against the scrutinee). The qualifier defaults to the last path segment;import path as nameoverrides it (e.g.import geometry.vec as gusesg.dot(..)). Two module imports whose qualifiers collide are rejected — rename one withasor import names directly.
The two brace-less forms are distinguished by what exists: if the whole path
names a module, it is a module import; otherwise the last segment is a name
imported from the module named by the rest. Qualified access disambiguates:
import c.{ X } and import a.b may coexist, with bare X resolving to
c’s definition and b.X resolving to a.b’s. A local binding shadows a
qualifier (let vec = ... makes a later vec.x a field access), and a
qualifier that collides with a declared or imported name is rejected.
Import paths resolve relative to the importing file’s directory: inside
app/main.pp, import geometry.vec.{...} refers to app/geometry/vec.pp.
Paths starting with std are global and refer to the embedded standard
library instead of files on disk. Import cycles are detected and reported.
Importing a type brings its methods with it — import geometry.vec.{ Vec2 }
makes both Vec2.new(...) and v.add(w) available; methods are in scope
wherever their type is. The import also gates
anonymous-record dispatch:
a literal like { x: 1.0, y: 2.0 } only adopts a type the module declares or
imports, while a value that already carries a nominal type — an imported
function’s return, say — dispatches its methods without the type’s name being
imported.
Visibility
Section titled “Visibility”A name is public unless it starts with _:
- a
_-prefixed function, type, or global is private to its module and cannot be imported; - a
_-prefixed module (file or last path segment) cannot be imported at all.
There is no other visibility control.
The standard library
Section titled “The standard library”The std/prelude/ modules (io, array, string, math, conv,
assert) form the implicit prelude: their public names are in scope
everywhere without an import. They can also be imported explicitly by their
bare name (import io.{ ... }) or std path.
The other standard-library modules — std.net, std.collections,
std.data.json — are not in the prelude. They are embedded in the
compiler but loaded only when a module imports them (transitively: a nested
std module may import another). See the
standard library reference.
Execution order
Section titled “Execution order”Each module’s top-level statements are gathered into a module initializer.
Initializers run first, in dependency order, then main is called if the
program defines one. Within a module, globals initialize in textual order, and
using a global before its initializer has run is a compile error.