Here, the . operator is used to construct fully-qualified names of
the form xxx.yyy.zzz. The context in which these are used
determines their meaning. For example, in the context of an import
statement the path xxx.yyy.zzz identifies a module
(e.g. std.ascii above). However, in the context of a function or
method invocation (e.g. io.print() above) the path identifies a
symbol (i.e. a named declaration in some module, such as a type or
function).

The goal of this proposal is two-fold. Firstly, to clarify the
distinction between module names and symbol names. Secondly, to
provide a syntax which is more familiar to systems programmers
(e.g. from C++ or Rust backgrounds).

Technical Details

A qualified name is a sequence of identifiers separated by ::
(e.g. std::ascii, std::ascii::to_string, etc). A qualified
module name is a qualified name where the last component identifies
the name and the preceding components (if any) constitute the
path. For example, in the module name std::ascii it follows that
std is the path, and ascii the name. A qualified symbol name is
a qualified name where the last component identifies the symbol and
the preceding components (if any) constitute a module name. For
example, in the symbol name std::ascii::to_string it follows that
std::ascii is the module name, and to_string the symbol name.
The above example in the proposed syntax would be:

NOTES: The separator :: is used for module names over . to
avoid overloading this with the field access operator. Likewise, ::
is adopted because it is familiar to C++ and Rust programmers.
Furthermore, it does provide a useful syntactic spacing between
modules and names.

Qualified names can be used in a number of different contexts:

Imports. When used in import statements, qualified names are
always module names (e.g. in import std::ascii, it follows that
std::ascii is a module name).

Expressions. When used in expressions, qualified names are
always symbols names (e.g.std::ascii::to_string() or
std::ascii::DEL).

Types. When used in a type, qualified names are always symbol
names (e.g. ascii:string and null|(utf8::char).

Another aspect of qualified naming is that a qualified name can be
fully-qualified or partially-qualified. For example, std::io is
the fully qualified name of the io module. In contrast, io::print
is a partially qualified name for the print method. This name is
considered partial because it employs an unqualified module name
(i.e. io rather than std::io). It follows from this that one can
choose to use fully qualified names to avoid import statements. For
example, we could rewrite the above without import statements as
follows: