Modules: files, submodules and imports
On this page
- A module is a namespace
- A module is a folder's files
- The mod line, then the imports
- Submodules: a namespace inside a module
- Folding a submodule's names in: use
- Sisters import each other; the parent is never imported
- import: using another module
- Folding names in: the use clause
- A module's default fold
- Importing under another name
- Re-exporting what you import
- Where an import is found
- An enum's variants as names of a module
- Types and blocks are namespaces too
- A member's name hides an outer one
- Names inside an enum
- Overload names: one name for several functions
- alias: a second name for a type
One file is plenty for a small program. As it grows, you want its parts in files of their own, some parts given names of their own so they cannot collide, and other people's code brought in. Cone's unit for all three is the module: the files of one folder, sharing one namespace. This page shows how to divide a program into modules and submodules, and how one module reaches another's names. Here is a small program in four files:
geometry/
congo.toml
src/
geometry.cone the module geometry, where the program starts
report.cone another file of geometry
shapes.cone 'mod shapes;': a submodule of one file
scaling/
scaling.cone 'mod scaling;': a submodule with a folder of its own
factor.cone another file of scaling
// src/geometry.cone
// The program's module. Its folder, src, holds the module's other files
mod geometry;
import stdio use *;
// shapes and scaling are submodules, below this one.
// use folds a submodule's public names into this module
use shapes;
fn main() {
// Point and area came from shapes, by the use above
let room = new Point(4, 5);
report(room);
// A submodule's names can also be reached by a path
report(scaling.twice(room));
}
// src/report.cone
// Another file of the module geometry: it has no mod line,
// because the folder it sits in already names its module
fn report(p Point) {
print <- p.x, " by ", p.y, ": area ", area(p), "\n";
}
// src/shapes.cone
// A file that opens with its own mod line is a submodule
mod shapes;
pub struct Point {
pub x i32;
pub y i32;
}
pub fn area(p Point) i32 {
p.x * p.y;
}
// src/scaling/scaling.cone
// A folder holding a file named for it is a submodule too
mod scaling;
// A submodule reaches its sister by importing her
import shapes use Point;
pub fn twice(p Point) Point {
new Point(p.x * factor, p.y * factor);
}
// src/scaling/factor.cone // A second file of scaling const factor = 2;
congo run, from inside the geometry folder, prints:
4 by 5: area 20 8 by 10: area 80
Try this: move shapes.cone into a folder of its own, src/shapes/shapes.cone, and run it again. Nothing that names shapes has to change.
A module is a namespace#
A module is a global namespace: it holds the program's types, functions, global variables and constants, its macros, and the modules below it, each under a name of its own. Every program or library has one main module, and that module may divide into submodules and import other modules for the capabilities it depends on. An import binds another module's name in this one, and its use clause may fold that module's public names in as names of this one.
A module is a folder's files#
A module's source files are the files of a folder. One of them is the folder's designated file, the file named for the folder, as geometry/geometry.cone is for geometry. It declares the module, with mod as its first statement. In a package the module's folder is src, and its designated file is named for the package, so src/geometry.cone plays that part above.
Point the compiler at the designated file and it finds the rest itself. Every other .cone file in the folder belongs to the same module, unless it opens with a mod of its own and so is a module of its own. So does every file of every subfolder that holds no designated file of its own, at any depth. Nothing lists them: putting a file in a folder is what brings it into a module, and moving it to another folder moves it to another module. A subfolder like that groups files without making a namespace of them, so a large module can be organized by topic with no new names to write and none to path through.
The folder names the module. A name written after mod may restate the folder's name and may not change it, because a name on the filesystem is one that any tool can read without parsing Cone. That name is what another module binds when it imports this one, the name a path through the module is written with, and the name its declarations carry in the symbols the linker sees.
Only the designated file declares the module, and it must. Because the declaration claims the folder, it comes before everything else in that file, and a module declares itself once. A file the folder brought in declares nothing: a file whose first statement is a mod is not brought in at all but is a module of its own, and a mod anywhere later in a file is an error.
The folder is the only thing that brings a file into a module. Nothing written in one file brings another in, so a file's module can always be told from where it sits. include, which once brought a named file in, is retired: it is a reserved word, and the compiler reports an include statement wherever it is written. Put the file in the folder of the module it belongs to instead.
Two files of one module may not share a name, however deep in the folder tree they sit, and a file belongs to one module: a file another module already holds cannot be brought into a second one.
A module's own name is one of the names in reach inside it. That matters where a local variable, a parameter or a type's member hides a name the module declares or a module it imports, since writing the module's name is the way to reach past the nearer one without renaming anything (a method named like an imported module is the common case):
mod counters;
let total = 5;
fn addTo(total i32) i32 {
counters.total + total; // the parameter hides the global; the module's name reaches it
}
A file the compiler is pointed at that is not its folder's designated file is a module of one file: a complete program or library, named after the file, with the files beside it none of its business. It opens with a mod line too, naming the file, as mod counters; opens counters.cone. For now a mod declaration in such a file may still rename its module.
The mod line, then the imports#
Every file that makes a module opens with its mod line: a folder's designated file, a one-file submodule, and a file the compiler is pointed at or an import reaches that is a module of one file. The line names what the filesystem already names, the folder or the file, so that the name is the first thing a reader of the file sees. A file that makes a module and opens with anything else, a mod trait included, is an error. The other files of a folder's module have no mod line, since the folder is what names the module they join.
A module's imports come right after its mod line, ahead of everything else it declares, so they are all in its designated file, and every dependency a module has is stated at its declaration. Comments may sit anywhere among them:
// geometry: points, and the shapes built on them
mod geometry;
// What this module uses
import stdio;
import collections use List;
pub struct Point {
pub x f32;
pub y f32;
}
An import written after any other declaration, a standalone use included, is an error. So is an import anywhere in the module's other files: they have no mod line, so there is no place for one. A use statement is not an import, and any file of the module may write one. So the top of the designated file says which module it opens and everything that module depends on, and the build tool, Congo, reads nothing more of it to find a package's modules and what they import.
Submodules: a namespace inside a module#
All the files of one module share one namespace, which is convenient until it is too crowded: every name may collide with every other, and every file may reach everything. A submodule divides a module further. It is a module of its own, inside the module whose folder holds it, with its own names and its own private parts.
A subfolder that holds its own designated file is a submodule. A subfolder that does not is only organizing files. One question decides both kinds (does it hold the file named for it?), and it is the same rule that made the folder a module in the first place:
geometry/
geometry.cone the module 'geometry'
shapes.cone a file of 'geometry'
detail/util.cone also a file of 'geometry': 'detail' holds no detail.cone
scaling/
scaling.cone the module 'geometry.scaling'
matrix.cone a file of 'geometry.scaling'
Its parent reaches into it by writing a path, the same way a path reaches into any module: scaling.twice(room) from inside geometry. What the parent may reach is what the submodule declares pub, because a submodule is a module and its private names are its own.
A submodule of one file needs no folder. A folder is only worth making when a module has more than one file, so a file of a module's folder whose first statement is a mod declaration is a submodule by itself, as shapes.cone is at the top of this page:
geometry/
geometry.cone the module 'geometry'
shapes.cone a file of 'geometry': no 'mod' of its own
units.cone 'mod units;': the module 'geometry.units'
scaling/
scaling.cone the module 'geometry.scaling'
matrix.cone a file of 'geometry.scaling'
units is a submodule in every respect scaling is: private to geometry unless written pub mod units;, reached by a path, units.metre(), folded by use units;, and imported by its sisters by name. Its own imports follow its mod, as a designated file's do. It does not join geometry, and none of geometry's files join it. It is named for its file: mod units; is written in units.cone, and any other name there is an error.
When it grows past one file, move units.cone to units/units.cone and put the new files beside it. Nothing that names it changes: not its name, not a path through it, and not the symbols the linker sees. A units.cone that declares a module beside a units/ folder that holds its own units.cone is an error, since that is two modules of one name.
A module must sit directly in its parent module's folder, as a file there or as a folder that is a direct child of it, so the module tree has the shape of the folder tree. A designated file further down, inside a folder that holds none of its own, is an error rather than a deeper submodule, and so is a file that opens with mod inside such a folder. A module is never declared inside a source file: nesting is by files and folders. (Only a package's generated include file writes a module block, for each submodule it reaches.)
A submodule is private to its parent unless it says otherwise. Written pub mod scaling; it can be named from outside its parent as well, as geometry.scaling.twice; written mod scaling; only geometry may name it. The pub goes on the submodule's own declaration, since the parent declares nothing about a subfolder or a file, and it means what it means everywhere else: this entry is visible outside this namespace. It opens a submodule to the rest of its package, never beyond: a program importing the package reaches a submodule's names only where the package's root re-exports them with pub use, since the package's include file declares each submodule in a private block.
Folding a submodule's names in: use#
A path on every use soon gets long. A submodule is never imported (its subfolder or its file is what brings it in), so a parent that wants its child's names bare writes a use statement naming the submodule. The clause after the name says which names, as an import's use does:
mod atlas;
use units { squareFeet as toFeet, metre, yard }; // a list, one renamed
use shapes but Hexagon; // every public name but one
use scaling.fixed; // a submodule's pub submodule, reached by a path
pub use detail; // detail's public names, re-exported as atlas's
A bare use shapes; folds every public name, as at the top of the page. Only a submodule's public names fold, and naming a private one is an error, whether in a list or after but, where there is nothing to leave out; so is the submodule's final or clone, which a fold of every name never brings. The folded names are this module's own, and private to it unless the statement is written pub use, which re-exports them: the parent's neighbours then reach them as names of the parent, while the submodule itself may stay private. A submodule is folded once: a second use of it is an error, so say everything in one.
A standalone use folds what a module reaches without importing it: a submodule, or an enum's variants. A module brought in by import is folded by that import's own clause, import collections use List;, and naming it in a use statement is an error: one spelling for each situation.
Sisters import each other; the parent is never imported#
Submodules often need each other: scaling above works on shapes' Point. Two submodules of one module may name each other by importing, writing the sister's name, never a path to her file:
mod scaling; import shapes use Point;
The module that holds them is the registry they are found in. They are public to each other and to it, and invisible outside it unless it publishes them, so an import written inside one of them looks the name up in the enclosing module and finds a sister there, with no file to search for and nothing loaded. It is the same mechanism that reaches a separately built library; only the registry that answers is different. Nothing arrives unasked, because a name nobody imported is not a name of the module.
The reach is one level and does not accumulate as you go down: a module sees its own parent's submodules, and not its grandparent's. A module inside such a tree is reached by its name, never by a path to its file: import "../wheels/wheels" is an error.
A submodule never imports its parent, or anything of it. The enclosing module's namespace holds more than its modules: what it declares, and what its own imports bind. But a module depends on each of its own submodules, so a submodule that depended on its parent would make the two depend on each other, which is an error naming both. Here scaling tries to import a Point its parent declares, and Congo refuses before it compiles anything:
mod scaling; import Point; // ERROR: Point is the parent's
congo: error: import loop between modules of package loopy: loopy -> loopy.scaling -> loopy. Imports between modules must not loop (loopy contains loopy.scaling; loopy.scaling imports Point of loopy at src\scaling\scaling.cone:2). A module may not depend on a module that contains it: move what they share into a sister both import.
The same holds for a module the parent imported, reached through the parent: the submodule imports it by its own name instead. And a module may not import the module that contains it: containment runs one way. The cure is the one the message gives, and the one the program at the top of this page uses: what a parent and its submodules share goes in a sister of theirs, which each of them imports. Why dependencies must run one way, at every size, is in Packages.
import: using another module#
A module reaches beyond itself in two ways. import reaches another Cone module, and is described here. extern reaches what is defined in another compiled unit, a separately built Cone package or code written in another language such as C, and is described in Talking to C.
import names a module, and binds that name in the importing module:
import stdio; import collections;
By default that name is all it brings. The imported module's names stay in their own namespace, so they are reached with a module-qualified name: stdio.print is the print declared in stdio. The period separates the module's name from the name inside it. It is the same operator that reaches a member of a value: one period, for a path through a namespace and for a member of a value alike. A qualified name reaches only what the module declared pub; every other name is private to its module. (A module may name a default fold on its mod line, which an import writing no use clause of its own folds in as well.)
More than one module may import the same module; it is loaded once, and what is known about it is shared by every module that imported it. A module imports another once. Writing the same import a second time does nothing, and a second import of the same module that says something different (it folds in other names, spells one differently, or is pub where the first is not) is an error, which names both. Say everything in the one import. A module's own submodule is not imported at all, and importing one is an error: its name is already there, and its names are folded with a use statement.
Folding names in: the use clause#
Keeping an imported module's names apart is usually what you want, since nothing in the importing module can collide with them. But a name used often reads better bare. An import may end with a use clause, which folds some or all of the module's public names into the importing module. A name may be for anything: a type, a function, a global, a macro. It is the same clause a global variable carries, with the imported module in the place of the variable's type, and the same one a submodule's use takes. Its forms:
import collections use List, Dict; // just these names
import collections use List as Seq; // one, spelled another way here
import collections use { // a long list, written as a block
List as Seq,
Dict,
};
import collections use * but Deque; // every public name but one
A module imports another only once, so these are four alternatives, not four lines of one module. as gives a folded name another spelling, for instance when its own is already in use. The use clause is the only way an import folds names in: import collections.*; and import collections.List; are errors, each naming the clause to write instead.
A folded name is private to the importing module unless the import says otherwise (Re-exporting, below). The rules that keep folding honest:
- Only public names fold. A name the module does not have, or does not make public, cannot be listed. Naming either after but is an error too: seen from outside a module a private name is as absent as a missing one, and * never folds it, so there is nothing to leave out. Nor does * fold the module's finalizer final or a clone, which belong to the module's own lifecycle, so naming one of those after but, or in a list, is an error as well.
- A folded name may not collide. Folding in a name the importing module already has for something else, whether it declared that name or folded it in from somewhere else, is an error: rename one with as, or leave it out with but.
- Nothing may be written in twice. Listing a name twice, or listing it from two modules that each re-export it, is an error. What a wildcard brings is different, since nothing was written twice: if two modules folded in with * each re-export one function, folding in both is fine, and so is listing the function from one while the other's * brings it too. The name is bound once, and if either fold made it public, it is public.
A module's default fold#
A package named for the one thing it provides would put that name in every path twice: bigint.BigInt. So a module may say, on its own mod line, what an importer gets when it imports the module and asks for nothing in particular. A module holding one thing then just becomes the thing:
mod bigint use BigInt;
pub struct BigInt {
pub digits i64;
}
pub fn helper(n i64) i64 {
n + 100;
}
A module that writes a bare import bigint; now has BigInt directly, and bigint as well, so bigint.helper still reaches what the default leaves out. With extends or is on the line, the clause comes last: mod bigint extends base use BigInt;.
- The list is the default, however many names it holds. mod units use metre, foot; makes a bare import fold both. * and * but may be written too. Adding a name later only adds to what importers see.
- An importer's own clause replaces the default entirely. import bigint use *, import bigint use helper or import bigint use * but BigInt folds what it says, and nothing of the default.
- Here use runs the other way. Everywhere else, use folds names into the module where it is written. On the mod line it names what importers fold.
- It is not an export list. pub still decides what is reachable, and every name the clause lists must already be a public name of the module: one the module has not got, or keeps private, is an error at the mod line. The clause only chooses which public names a bare import folds.
- The importer decides the rest. The names are the importer's, private to it unless it writes pub import bigint;, which re-exports them with bigint. A default name that collides with one of the importer's own is an error there, settled by writing a clause of its own. For the same reason neither as nor pub may be written in the clause on a mod line.
The default applies wherever a module is imported with no clause of its own: a package, a sister, or a module found by its file. A use of a submodule is not an import, and still folds every public name.
Importing under another name#
as after the module binds it under a name of the importer's choosing instead of its own:
import collections as coll;
fn main() {
var list = new coll.List[i32];
list <- 1, 2, 3;
}
The new name is used exactly as the module's own would have been: for its types, its functions, and the modules inside it. The module's own name, collections, is not a name here at all. Every form of import takes as (a name or a quoted path, a package or a sister, pub or not), and it comes before any use clause, which folds what it would have folded without the rename: import geometry as geo use dist2 as d2;. A module's default fold still applies to a renamed import that writes no clause, and pub import units as u; makes u a public name of the importing module, as Re-exporting describes for the module's own name.
The rename is a name of the importing module like any other, so it may not collide with one of its declarations, with the name another import binds, or with a name a use clause folds in. A module is still imported only once, so a second import of it is an error whatever name each binds it under, and a use clause may not fold the module's own name back in, which would undo the rename.
The usual reason to rename is a name that is wanted for something else. A type's field or method hides a module of the same name inside the type, its methods' signatures included, so a method named after the module it returns a type of needs the module imported under another name; see Types and blocks are namespaces too.
Re-exporting what you import#
What an import brings in belongs to the module that wrote it. A module importing that module sees only what it passed on, which it does by writing pub:
mod engine; pub import wheels; // 'wheels' is a public name of engine, so 'engine.wheels.turn' resolves import tyres pub use *; // every name folded in from tyres is public, and 'tyres' is not import bigint; // while this is engine's own business
pub before import reaches every name the import binds: the module's own name, and each name it folds in. pub use reaches only the names the use clause folds in, so a module can pass on a library's names without passing on the library. Writing both is allowed and means what pub import means alone. pub says what it says everywhere else, that this name is visible outside the module that holds it, and it never makes public something that was not public where it was declared.
A name made public this way travels on: a module that imports engine with use * receives what engine passed on, and nothing that it kept for itself. That holds round a cycle of imports too: where two modules import each other, what each re-exports reaches the other, as it would if neither imported the other back. What pub means for everything else a module holds is in Public and private.
Where an import is found#
An import names a module, and the name is looked for in this order:
- A sister, since the enclosing module is asked before any file is looked for. So inside a submodule whose sister is named stdio, import stdio reaches her.
- A file beside the importing file, with .cone assumed when no extension is given, or, if no file of that name is there, the designated file of a folder of that name (useme/useme.cone).
- The package search path: each folder given to the compiler with --path, in order, and then the packages folder, which holds one folder per package, whose source is its src folder: src/name.cone is the designated file of the package's module, and the folder's other files and submodules are the package's by the module rules. The packages that come with Cone live there: core, which every module imports without asking, stdio, collections and the rest. The compiler finds its packages folder from where it is installed (the nearest packages folder holding core, in the compiler's own folder or one above it), and the CONE_PACKAGES environment variable names another. So the name of an imported module may correspond to an externally hosted library, and a package in a --path folder is found ahead of the packages folder's package of the same name.
The name may also be a file path. One that holds characters a Cone name may not (periods, slashes) is written in double quotes, with escape sequences where needed. It may be absolute, "/libs/math/trigfns", or relative to the importing file, "nomnom/useme" or "../lib/useme", which lets related source files be grouped in nested folders.
A package built from a build description searches for nothing. This is how Congo builds every package. After a sister, an import is found where the description's import line for its name says, and an import the description provides nothing for is an error, import stdio included. The file such a line names is only declared, as another package's include file is: its name is the import's. Its functions are called, and its globals read, in the object of the package it stands for, which is linked in beside the program's; only its inline bodies are compiled into the importer. It is a module file like any other, so it may import in its turn: those imports are found where the description's package lines say, and what they bind is the include file's own, not the importer's.
A program compiled without a build description finds its imports by the search path instead. A module found on the search path is compiled into the program that imports it. One found beside the importing file is only declared, so a program importing one cannot be linked; use a package for a program of more than one module.
An enum's variants as names of a module#
An enum's variants are names of the enum, reached as Colors.Red. A module that wants them as names of its own writes a use statement naming the enum, and optionally which variants: the same statement, with the same clause, that folds a submodule's names in.
enum PhoneType { Home, Work, Mobile; }
enum Event { KeyEvent; QuitEvent; MouseEvent; }
enum Point { struct XyPoint {pub x i32;} struct AngPoint {pub a i32;} struct Other {} }
enum Colors { Red, Green, Blue; }
use PhoneType; // Home, Work and Mobile, bare
use Event KeyEvent as Key, QuitEvent; // only these, one of them renamed
use Point {XyPoint, AngPoint}; // a list may be spread over lines in braces
use Colors but Blue; // every variant but one
let c Colors = Red;
After use PhoneType;, Work means PhoneType.Work. The module that declares the enum is no exception: it writes use like any other module to have the variants bare. A use folds variants and nothing else of the enum, and a variant it leaves out is still reached through the enum. The enum may be named through a module, use shapes.Colors;, and one variant of it named alone, use shapes.Shape Circle;. A folded name must not be one the module already has for something else, whatever brought that one in; two enums that each have a Quit are both folded by renaming one or leaving it out. Nor may a module fold the same variant in twice itself: writing use Colors; twice is an error. The same variant arriving by a wildcard import is the name the module already has, so use Option; is fine beside the Some and None every module already has. A pattern needs no use: it names a variant bare (match on variants).
The names are this module's own and private to it unless the statement is written pub use, as pub use Suit;. That is how a module re-exports an enum's variants: another module reaches them through it, shapes.Red, and an import of it with use * brings them in bare. The core library writes pub use Option; and pub use Result;, and every module imports core with every name folded, which is why Some, None, Ok and Error are bare in every program (Naming a variant).
Types and blocks are namespaces too#
A module is not the only namespace. A struct or an enum holds the names of its fields and methods, its static functions, its static variables and the types declared inside it, and an enum's namespace holds its variants, which is why a variant is reached through its enum unless a module folds it in. Inside a type, a name may be used before it is declared. What a type's names may be, and who may reach them, is in Public and private.
A member's name hides an outer one#
Inside a type, a field's or method's name hides any name of the same spelling further out, as an inner name hides an outer one everywhere. That includes a module the type's module imports, and it holds in the methods' signatures as well as their bodies. A signature has no self to reach a field or method through, so a module written there under a member's name is an error:
mod scene;
import collections;
struct Model {
parts i64;
fn collections(self &) collections.String { // ERROR: in Model, 'collections' is this method
collections.String.empty();
}
}
Error 1019: collections is a method, and there is no self here to reach it through. Inside Model its name hides the module this module imports as collections. Import the module under another name, as in 'import collections as collectionsmod;', or reach it through this module's own name, as in 'scene.collections'.
The error names the two ways round it. One is to import the module under another name, which no member hides:
mod scenery;
import collections as texts;
struct Model {
parts i64;
fn collections(self &) texts.String {
texts.String.empty();
}
}
The other is to reach the module through the module's own name, which is one of the names in reach inside it and which no member hides either:
mod staging;
import collections;
struct Model {
parts i64;
fn collections(self &) staging.collections.String {
staging.collections.String.empty();
}
}
Names inside an enum#
Inside an enum's braces, every name the enum declares is bare: its variants, and its fields, methods and functions. That holds for a variant's own body too, since it is written there, so a method of one variant names another without the enum in front:
enum Traffic {
struct Stop {
fn next(self &) Traffic {Go(5);} // Traffic.Go
}
struct Go {
pub delay i32;
}
}
A name the variant declares itself comes first: a field of Go spelled like a sibling variant is, inside Go's methods, the field. One exception: a variant of a generic enum reaches the enum's methods through self, as self.name(), since it receives them only when the enum is instantiated. In a generic enum the enum's bare name, and each variant's, is the instance being defined, at the enum's current type arguments: inside enum Maybe[T], Maybe is Maybe[T], and a variant's No builds Maybe.No[T] (Generic types). A variant that an extension copies from its base (Extending an enum) keeps what its names meant where it was written, so it still means the base's variants. Where an enum's methods may be written, and which members every variant shares, is in Traits.
A type may declare static functions among its methods: functions that do not work on one value of the type. What makes one static is that it has no self parameter. It is not marked static: that word declares a static variable, and before a function it is an error. Outside the type, the type's name qualifies it:
struct Counter {
count i32;
pub fn zeroFactory() Counter {
new Counter(0);
}
}
var cnt = Counter.zeroFactory();
Overload names: one name for several functions#
Every function and method has its own name, unique within its namespace. Several of them may additionally share an overload name, when they perform essentially the same operation on a different number or different types of values. Each declares the shared name by writing overload and that name between its own name and its signature:
struct Comp {
pub re f32;
pub im f32;
}
// 'addFloat' is this function's own name. It also overloads the shared name 'add'.
fn addFloat overload add(a Comp, b f32) Comp {
new Comp(a.re + b, a.im);
}
// 'addComp' is this function's own name. It overloads the same shared name.
fn addComp overload add(a Comp, b Comp) Comp {
new Comp(a.re + b.re, a.im + b.im);
}
struct Rect {
pub w f32;
pub h f32;
pub fn areaOf overload area(self) f32 {
w * h;
}
pub fn areaScaled overload area(self, scale f32) f32 {
w * h * scale;
}
}
let c = new Comp(1., 2.);
add(c, 0.5); // addFloat
add(c, c); // addComp
let r = new Rect(2., 3.);
r.area(); // 6: selects areaOf
r.area(4.); // 24: selects areaScaled
r.areaScaled(4.); // 24: the method called directly by its own name
A shared name may only be used as the name of a function or method being called. Everywhere else the own name is used: it is the name a reference to the function is taken by (&addFloat), and the basis of the function's linker name. A shared name is as visible as the functions that overload it, so they must agree: the first declared decides whether the shared name is pub, and a later function that disagrees is a compile error, because callers of a public name would otherwise reach code its own declaration keeps private, or a public function would be hidden behind a private name.
When a shared name is called, the compiler tries every function that overloads it and requires exactly one of them to accept the call's receiver and arguments, allowing for default parameter values and permitted implicit coercions. No match is an error, and more than one is an ambiguity error. An exact match is not preferred over a match requiring coercion, needing fewer coercions does not win, declaration order never breaks a tie, and return types take no part in the choice. A caller resolves an ambiguity by calling a function's own name, or by converting arguments so that only one function accepts them. So two functions may not declare the same own name, a shared name may not also be some other declaration's name, and two functions that overload one shared name may not accept exactly the same arguments: they should vary in their number of parameters or in their types. A generic function or method joins a shared name only when every type parameter is bound to a function signature (Generic overloads).
Every block inside a function or method is a namespace for its local variables and parameters, with three rules of its own. Its names are unique: no two variables in one block may be declared with the same name, though a variable may shadow one of the same name in an outer block. They are order-dependent: a local variable may not be used until after it is declared. And they are private: only the block's own logic may see and use them, which is isolation at the smallest layer.
alias: a second name for a type#
Note: an alias of a type is implemented. An alias of a function, a global, a module or a method is not yet.
A type spelled out in full can be long. alias, declared at a module's top level and nowhere else, gives it a second name:
alias Noderef = Rc[mut, Node];
Wherever Noderef is used in this module, it means Rc[mut, Node]. What follows the = is resolved when the program is compiled, and is never a computation: an alias names something that already exists, it does not make a value.
An alias is a second spelling for one type, not a second type: it creates no distinct type, and nothing is asserted by using one name rather than the other. Because it is a second spelling and not a second thing, it also qualifies what it names: where alias Sample = Reading; names a struct, Sample.make(1) reaches the same static function Reading.make(1) does. To declare a type of your own that adds methods to an existing one, which is a distinct type, see Enriching a concrete type; to name a function's signature, see Naming a signature.
An alias may be declared pub, which makes the name visible outside its module; the type it names keeps its own visibility, so an alias never lends out something private. One alias may name another, but a chain that comes back to itself names no type at all, and is refused where it is written. typedef, the statement's former spelling, is retired: it remains a keyword, and the compiler reports a typedef statement where it is written.
You can now divide a program into modules and submodules, and reach any module's names, by path or folded in. Every one of those rules turned on one word, pub; the next page is about what it means to keep a name private, and why the module is where that line is drawn.