Modules and imports
Importing
Section titled “Importing”import "./thing.knip";import "../lib/shapes.knip";import "./thing.knip" as util;Paths are relative to the importing file. The .knip extension is optional. Imports go at
the top of the file.
Namespacing
Section titled “Namespacing”An import brings its symbols in under a namespace — the file’s basename, or the alias:
import "./shapes.knip";import "./shapes.knip" as geo;
temp p: num = shapes.perimeter(4, 3);temp q: num = geo.perimeter(4, 3);Visibility
Section titled “Visibility”Only public symbols cross a file boundary:
public sides: num = 3;private secret: num = 42;
public proc perimeter(side: num, count: num) -> num { return side * count;}import "./lib.knip";
temp a: num = lib.sides; # oktemp b: num = lib.secret; # error: 'secret' is privateImports are not re-exported
Section titled “Imports are not re-exported”If a.knip imports b.knip, a file that imports a.knip cannot reach b through it:
import "./b.knip";public proc quad(n: num) -> num { return b.double(b.double(n)); }import "./a.knip";
a.quad(2); # ok — a uses b internallya.b.double(2); # error — b is not re-exportedEach file names its own dependencies. There is no transitive namespace.
The import graph
Section titled “The import graph”Each file is read and parsed exactly once, no matter how many files import it. Unresolvable paths and import cycles are reported against the file that did the importing, with a source span.
Resolvers
Section titled “Resolvers”Import resolution is pluggable. The CLI resolves against the filesystem; the VS Code extension resolves against open documents so unsaved edits are picked up; an embedding can supply virtual files, which is how a browser playground would work.
If you are embedding the compiler, you pass a resolver — see Embedding the compiler.