Modules & imports
import std.time # binds `time`
import std.time as t # choose the name
import "./helpers.ecko" # binds `helpers` by file stem
import "./helpers.ecko" as h
import url # a vendored package
Three kinds of import, one syntax. A bare name is a package; a quoted path is a file; std.* is built in.
Aliasing
import std.time as t
fn time() = t.now() # your own `time` is free
Worth doing whenever a module name collides with something you want to call yourself. A module binds the last segment of its path, so import std.str binds str. It is named str and not string for exactly that reason: a module named after the string() converter would shadow it for the rest of the scope.
Private by default
Only export-marked definitions are visible to an importer.
export fn clean(s) = trim(lower(s)) # public
fn internal(s) = s # private
The export modifier is inline, on the declaration. An older export { a, b } block form existed; ecko fix --migrate --only=exports rewrites it.
Re-exporting
A multi-file package curates its public surface in its entrypoint:
export * from "./validators.ecko" # the whole exported surface
export { with_headers } from "./util.ecko" # just this one
export { esc as escape } from "./e.ecko" # renamed
export import "./shared.ecko" # bind locally AND re-export
This is how a package presents one flat namespace over several files while keeping helpers private - export { with_headers } publishes that function and leaves its siblings internal even though they are exported where they are defined.
ecko doc follows these statements with the same selectivity, so the generated reference is the real public surface.
Shadowing a built-in
Legal, and sometimes necessary - a package exporting get, join or string. Reach the original through core.*:
export fn get(c, key) { ... }
fn internal(m, k) = core.get(m, k) # the builtin
ecko check warns on the shadow when the file also calls the name, since that is usually accidental.
Assigning to an imported name
json = x after import std.json does not replace the module. It makes a new binding in the current scope, the same as assigning to a built-in name, so inside a function it is a local of that function and the module is untouched for every other caller. ecko check warns on it (shadows-import). See Variables.
Circular imports
Detected and reported rather than hanging or half-initializing a module.
Capabilities
A std.* module's gated functions check the capability set of the calling package. Root code holds full authority; a package holds what its importer granted. So import std.fs in a package is not a permission - the grant is. See Capabilities & gating.