Expand description
The imports every module gets without writing them.
Zelkova’s standard library is reachable from a module that imports nothing:
Int, Bool, True, + and <| resolve in a file whose only declaration is
x = 1 + 2, and Maybe, Just, List.map and String.length resolve there
too. The default
imports is the rule; this
module is the list it names, plus the two questions asked of that list.
§Why the list lives here and not in canonicalization
Two phases need it, for two halves of one behaviour.
canonical::environment::new_environment
turns each entry into a [parser::Import] and runs it through process_import
ahead of the module’s own, so a name arriving implicitly is indistinguishable
from one written by hand. dependencies::ModuleWalker::new
adds the matching edge to the import graph, because a module can only resolve
against an Interface that already exists — so
Basics has to be checked before the module that never named it, and nothing
but the dependency graph decides that order.
Neither phase owns the other, and a copy of the list in each would be two lists. So it sits beside them both, in a module that is the list and nothing else.
§Where the cycle does not happen
Basics cannot implicitly import Basics, and Maybe implicitly importing
Result importing Maybe is exactly the loop
dependencies exists to reject. What keeps the
graph acyclic is a property of the package, not of any one entry: zelkova-core
— the package the eight belong to — is the exception, and it is all-or-nothing.
No module of it receives any of the eight, not the eight themselves and not the
facades they are built from, so nothing in the package can ever close a loop
through an implicit edge.
PackageName::is_core is that question,
asked of the package’s own name rather than of its module names:
resolve::visible_modules rejects any
package other than zelkova-core that declares one of the eight, before any module
of it is compiled, so a package this is true for is zelkova-core by construction
rather than merely by what it happens to contain. Every module of every other
package receives all eight, whatever it imports and whatever imports it. The
default imports is the rule in
full, and DEC-17 is why it is scoped to the
package rather than judged from the import graph.
zelkova-core’s own modules write every import they use — Basics.zel,
Maybe.zel, Result.zel and Bitwise.zel already do — which is what makes the
exception affordable: nothing in the package spends an entry it does not receive.
implicit_imports and dependencies::add_default_import_edges are asked the
same question rather than each deriving their own answer: both take an is_core
argument that traces back to one PackageName::is_core call, so a package is
exempt to new_environment exactly when it is to ModuleWalker::new.
§An implicit import is never a diagnostic
A synthesised import has no source text behind it, so its span is
[NodeSpan::none] and any error raised from it would render with no caret — a
message about an import line the user never wrote. implicit_imports
therefore applies an entry only when the package genuinely provides what that
entry asks for: the module has to have been checked already, and a Maybe(..)
or List entry additionally has to find that type declared in the interface,
and a Maybe(..) entry has to find its constructors exposed.
Anything missing is left out, and the names it would have brought fail at the
place they are used, with a caret under them.
That is what makes the list land in stages without a second change: List
and Char are .ignored files under std/core/src today, so their entries do
nothing. Each starts working on the day its module compiles — String’s and
Task’s already do, since LANG-73 and LANG-74.
Structs§
- Default
Import - One entry of the default import list.
Enums§
- Unqualified
- What a default import makes available unqualified, beyond the qualified spelling that every import provides.
Constants§
- DEFAULT_
IMPORTS - The eight modules every Zelkova module behaves as though it began by importing.
Functions§
- implicit_
imports - The imports a module gets without writing them, given what it did write and which interfaces are available to it.
- is_
default - Whether
moduleis one of the modules the default imports name.