Learn · step 3 of 8
One keyword, and your own namespace
import loads a module and gives you its qualified name. The list or the glob written after the path, ::{...} or ::*, is what shortens that, and you write it, so no library can change what a name in your file means by adding an export of its own.
What the rule says
R-2b One keyword loads a module and names what it brings into scope, written by the consumer.import XloadsXand leaves its names qualified (X::f);import X::{a, b}also bringsaandbinto unqualified scope;import X::*brings every exported name.pub import Xre-exports the module, never names.R-2b SPEC.md, line 38
Run it
This step is grounded in samples/iyi/modules.iyi, 124 lines in the repository, read out of the tree when this page is built.
Run it in your browser from the module the compiler built for it.
Two steps in, the library from step one has a shape: three definitions, two of them exported, all of their types written down. Bringing that into a file of your own takes one line, and the line says how much of it you want.
module samples/modules
# R-1: `import` names a module and loads it at most once, so the import graph
# is a DAG rather than textual inclusion. Bare, it brings the module into
# scope under its qualified name and nothing more: after `import app/greeter`
# alone, the module's names are reachable only as `App::Greeter::Greet` and
# `App::Greeter.polite`.
import app/greeter::*
import app/formal::{address}import names a module and loads it at most once, so the import graph is a DAG
rather than textual inclusion. Written bare, import app/greeter gives you the
module under its qualified name and nothing more: polite is
App::Greeter.polite and nothing shorter.
What you write after the path is what shortens it. ::* brings every exported
name, ::{...} brings the ones listed, and the library does not get a say. That
is the whole of R-2b, and it is a smaller idea than it sounds: a module's names
reach your file only through a line in your file, and the glob is spelled out, so
"every name" is visible on the line where it happens.
The list on the second line is the interesting one. app/greeter and
app/formal both export title, so taking both whole would make every bare
title in the file ambiguous. It takes exactly address from the second module
and leaves that module's title behind. That list is a repair rather than mere
tidiness.
Where the ambiguity gets reported#
Widen that second import to import app/formal::* and nothing happens. Two
modules exporting one name is not a mistake. The error arrives later, at the
first bare title you write, because that is the first thing that could mean
either.
Then add your own def title to the file and it compiles again. A local
definition beats an imported one, silently. That silence is deliberate: it is
what stops a library breaking your build by adding an export.
The wall a qualified name does not climb#
Qualifying a name is not a way around the import. Call App::Greeter.polite
from a file that never imported app/greeter, because some other file in the
program did, and the compiler does not let the rest of the program's imports
leak into yours:
Error: `App::Greeter` is not imported here — it is in the program only because
some other file imported it. Add an import in this file, or have a module this
file imports re-export it with `pub import` (SPEC.md R-1)It names the rule and both ways out. The rules are new, so the compiler teaches them at the moment they are broken, which is the only place a person is actually paying attention.
Now forget the names#
Do it the other way around. Import the module bare, and call one of its exported functions the way every other language would let you.
Break it
an unqualified name no import brought in
# Breaks R-2b: a bare `import` brings a module into scope under its qualified
# name and nothing more. Unqualified names arrive only through the list or the
# glob on an import line, `::{...}` or `::*`, which the consumer writes, so
# importing a module never quietly changes what a name in this file means.
# `polite` is exported and still not in scope here.
module diagnostics/r2b_name_not_imported
import dep/greeter
puts polite("iyi")# A module the break programs import. Its surface is deliberately narrow: one
# exported function, one exported trait, and one function left unexported so a
# consumer has something it is not allowed to reach.
module dep/greeter
pub trait Greet
abstract def greet : String
end
pub def polite(name : String) : String
"#{greeting}, #{name}."
end
# No `pub`, so this is the module's own business. It is not in the module's
# declarations, which is what makes reaching for it from another module an
# error rather than a style question (SPEC.md R-1, IV.2).
def greeting : String
"Hello"
end$ IYI_PATH=<iyi>/src iyi build --no-codegen --no-color records/break/r2b_name_not_imported.iyi Showing last frame. Use --error-trace for full trace. In records/break/r2b_name_not_imported.iyi:10:6 10 | puts polite("iyi") ^----- Error: undefined method 'polite' for Diagnostics::R2bNameNotImported:Module `polite` is exported by `dep/greeter`, and this file has not brought it into scope. Import it by name, `import dep/greeter::{polite}`, or call it as `Dep::Greeter.polite` (SPEC.md R-2b)
import X loads X and leaves its names qualified (X::f); import X::{a, b} also brings a and b into unqualified scope; import X::* brings every exported name. pub import X re-exports the module, never names.SPEC.md, line 38. Recorded run, exit code 1, iyi 0.17.0 (built on Crystal 1.22.0-dev [fe3db8354] (2026-10-14)), commit fe3db8354f56. Regenerate with npm run record:diagnostics.What the rule bought
A bare import never changes what your unqualified names mean, so a library cannot capture a name in your file by adding one to itself, and a local definition beats an imported one silently. Both are what make it safe to depend on a module you do not control, which is the position every consumer of an artifact is in.
Standing on
samples/iyi/app/greeter.iyi- the module both imports name, exporting a
title samples/iyi/app/formal.iyi- the second module, exporting a
titleof its own samples/iyi/modules.iyi- the glob, the list, and where an ambiguous name is reported
README.md- the diagnostic for a module this file never imported, quoted with its lines on the block