Imports

An import declaration brings names from a package into the current scope. Every path starts with a package, and nothing from another package — not even a fully qualified path — can be named without an import.

import-decl = "import" package { "::" segment } [ "::" ( "*" | "{" name { "," name } [ "," ] "}" ) ] ";"
segment     = identifier | "#" identifier
name        = identifier | "#" identifier

Forms

FormBrings inUsed as
import Pkg::Item;one root item of PkgItem
import Pkg::Mod;a module of PkgMod::Item
import Pkg::Mod::Item;one item of a moduleItem
import Pkg::{ A, B, #target };several items or modules from one placeA, B, #target
import Pkg::Mod::{ A, B };several items of one moduleA, B
import Pkg::*;every public root item and top-level module of Pkgeach by its name
import Pkg::Mod::*;every public item of a moduleeach by its name
import Pkg;Pkg's module named Pkg, when it has onePkg::Item
import Io::PrintLine;                  // a root item
import Core::{ #target, int8 };        // a compile-time value and a primitive's constants
import Tally::{ Counter, Level };      // two items from one package
import Shapes::Square;                 // a whole module
import Shapes::Square::Perimeter;      // one item of a module
import Shapes::Circle::*;              // every public item of a module

func Main() -> int {
    var counter = Counter(3);
    counter.Tick();
    PrintLine("{} {}", counter.Count(), Level::Low == Level::Low);
    PrintLine("{} {} {}", Square::Area(5), Perimeter(5), Diameter(2));
    PrintLine("{} {}", int8::Max, #target.pointerBits);
    return 0;
}

Importing a module rather than its items keeps calls qualified, which is the better choice whenever a bare name would be unclear or would collide: Square::Area(5) says which Area is meant.

import Pkg; on its own names a module, not the package: it works only when Pkg declares a module called Pkg (pub module Pkg { … }). Otherwise it is an error — import an item or a module instead:

error: import 'Io' does not name a module
  help: import an item instead, for example 'import Io::Name'

The first segment is a package

The first segment of every import is a package's import name:

  • the current package's own Name from Rux.toml — a package imports its own nested modules this way, as in import Geometry::Shape::Circle;;
  • or a key under [Dependencies]. The key is usually the package's name, but it may differ, which renames the package for imports: with Shapes = { Package = "Geometry2", Path = "../Geometry2" }, the example above imports from Shapes.

A first segment that is neither fails before analysis starts:

error: package 'Shape' is not listed in [Dependencies] of '…\Rux.toml'
  note: the import requires a package dependency with the same import name
  help: add the package under [Dependencies] or correct the import path

The remaining segments walk the package's modules. An item or module that is not there is reported as name 'Gauge' was not found in package 'Tally' or module 'Counter' was not found in package 'Tally'.

Qualified paths need an import

A path in an expression or a type starts from a name already in scope. Another package's name is never in scope by itself, so a fully qualified call does not work:

func Main() -> int {
    return Tally::Clamp(12);   // error: name 'Tally' is not defined in this scope
}

Import the item, or the module that contains it, and qualify from there. Inside one package, every root item and every top-level module is already in scope, so Shape::Circle::Area(2.0) works without an import from anywhere in the package that declares Shape — see Name lookup.

Compile-time values and primitive constants

Imports also reach two kinds of name that look built in but are declared by the Core package:

  • Compile-time values and directives are imported by their # names: import Core::{ #target, #build, #Error };. Without the import, #target.os fails with error: name '#target' is not defined in this scope. See Compile-time context.
  • Associated constants of primitive types — int8::Max, uint::Bits, float64::NaN — come from Core's declarations of those types, so the type must be imported: import Core::int8;. The primitive type itself is usable without any import; only its constants need one. Without it:
error: 'Max' not found in extend for type 'int8'

The full list of primitive constants is in Primitive types; how a package declares them is in Intrinsics.

Where imports go

An import is a declaration. It may appear at the package root, inside a module body, and inside a when branch; in a when match arm the trailing ; is optional. An import inside a module applies to that module and the modules nested in it. It cannot appear inside a function body:

error: expected an expression before 'import'

By convention each file starts with the imports it uses.

Root imports are shared between files.
rux 0.4.0 places an import written at the top of a file into the package root, so the other files of the package can see it too. Do not rely on that: write in each file the imports that file uses. Associated constants are already strict about it — int8::Max is found only in a file that itself imports int8.

Glob imports

import Pkg::*; and import Pkg::Mod::*; bring in every public name at that level — items, and with the package form also its public top-level modules. Private declarations are skipped silently, so a glob never fails on them; naming a private item explicitly does fail:

error: function 'Clamp' is private to package 'Tally'
  help: add 'pub' to the declaration of 'Clamp'

A glob makes it hard to see where a name came from, and grows when the package does. Prefer a list.

Conflicts

An imported function joins the overload set of a function with the same name already in scope. Two candidates that accept the same arguments make every call ambiguous:

error: call to 'Validate' is ambiguous: 2 overloads accept argument types ()
  help: rename one of the overloads, or remove a default value that makes them overlap

Import the module instead and qualify the call.

See also