Skip to main content

Module default_imports

Module default_imports 

Source
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§

DefaultImport
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 module is one of the modules the default imports name.